diff options
Diffstat (limited to 'src/shared')
| -rw-r--r-- | src/shared/command.h | 335 | ||||
| -rw-r--r-- | src/shared/crypto.cpp | 944 | ||||
| -rw-r--r-- | src/shared/cube.h | 68 | ||||
| -rw-r--r-- | src/shared/cube2font.c | 556 | ||||
| -rw-r--r-- | src/shared/ents.h | 237 | ||||
| -rw-r--r-- | src/shared/geom.cpp | 257 | ||||
| -rw-r--r-- | src/shared/geom.h | 1828 | ||||
| -rw-r--r-- | src/shared/glemu.cpp | 355 | ||||
| -rw-r--r-- | src/shared/glemu.h | 180 | ||||
| -rw-r--r-- | src/shared/glexts.h | 488 | ||||
| -rw-r--r-- | src/shared/iengine.h | 583 | ||||
| -rw-r--r-- | src/shared/igame.h | 130 | ||||
| -rw-r--r-- | src/shared/stream.cpp | 1264 | ||||
| -rw-r--r-- | src/shared/tools.cpp | 244 | ||||
| -rw-r--r-- | src/shared/tools.h | 1408 | ||||
| -rw-r--r-- | src/shared/zip.cpp | 588 |
16 files changed, 9465 insertions, 0 deletions
diff --git a/src/shared/command.h b/src/shared/command.h new file mode 100644 index 0000000..475e492 --- /dev/null +++ b/src/shared/command.h @@ -0,0 +1,335 @@ +// script binding functionality + +enum { VAL_NULL = 0, VAL_INT, VAL_FLOAT, VAL_STR, VAL_ANY, VAL_CODE, VAL_MACRO, VAL_IDENT }; + +enum +{ + CODE_START = 0, + CODE_OFFSET, + CODE_POP, + CODE_ENTER, + CODE_EXIT, + CODE_VAL, + CODE_VALI, + CODE_MACRO, + CODE_BOOL, + CODE_BLOCK, + CODE_COMPILE, + CODE_FORCE, + CODE_RESULT, + CODE_IDENT, CODE_IDENTU, CODE_IDENTARG, + CODE_COM, CODE_COMD, CODE_COMC, CODE_COMV, + CODE_CONC, CODE_CONCW, CODE_CONCM, CODE_DOWN, + CODE_SVAR, CODE_SVAR1, + CODE_IVAR, CODE_IVAR1, CODE_IVAR2, CODE_IVAR3, + CODE_FVAR, CODE_FVAR1, + CODE_LOOKUP, CODE_LOOKUPU, CODE_LOOKUPARG, CODE_ALIAS, CODE_ALIASU, CODE_ALIASARG, CODE_CALL, CODE_CALLU, CODE_CALLARG, + CODE_PRINT, + CODE_LOCAL, + + CODE_OP_MASK = 0x3F, + CODE_RET = 6, + CODE_RET_MASK = 0xC0, + + /* return type flags */ + RET_NULL = VAL_NULL<<CODE_RET, + RET_STR = VAL_STR<<CODE_RET, + RET_INT = VAL_INT<<CODE_RET, + RET_FLOAT = VAL_FLOAT<<CODE_RET, +}; + +enum { ID_VAR, ID_FVAR, ID_SVAR, ID_COMMAND, ID_ALIAS, ID_LOCAL }; + +enum { IDF_PERSIST = 1<<0, IDF_OVERRIDE = 1<<1, IDF_HEX = 1<<2, IDF_READONLY = 1<<3, IDF_OVERRIDDEN = 1<<4, IDF_UNKNOWN = 1<<5, IDF_ARG = 1<<6, IDF_EMUVAR = 1<<7 }; + +struct ident; + +struct identval +{ + union + { + int i; // ID_VAR, VAL_INT + float f; // ID_FVAR, VAL_FLOAT + char *s; // ID_SVAR, VAL_STR + const uint *code; // VAL_CODE + ident *id; // VAL_IDENT + }; +}; + +struct tagval : identval +{ + int type; + + void setint(int val) { type = VAL_INT; i = val; } + void setfloat(float val) { type = VAL_FLOAT; f = val; } + void setstr(char *val) { type = VAL_STR; s = val; } + void setnull() { type = VAL_NULL; i = 0; } + void setcode(const uint *val) { type = VAL_CODE; code = val; } + void setmacro(const uint *val) { type = VAL_MACRO; code = val; } + void setident(ident *val) { type = VAL_IDENT; id = val; } + + const char *getstr() const; + int getint() const; + float getfloat() const; + bool getbool() const; + + void cleanup(); +}; + +struct identstack +{ + identval val; + int valtype; + identstack *next; +}; + +union identvalptr +{ + int *i; // ID_VAR + float *f; // ID_FVAR + char **s; // ID_SVAR +}; + +typedef void (__cdecl *identfun)(); + +struct ident +{ + uchar type; // one of ID_* above + union + { + uchar valtype; // ID_ALIAS + uchar numargs; // ID_COMMAND + }; + ushort flags; + int index; + const char *name; + union + { + struct // ID_VAR, ID_FVAR, ID_SVAR + { + union + { + struct { int minval, maxval; }; // ID_VAR + struct { float minvalf, maxvalf; }; // ID_FVAR + }; + identvalptr storage; + identval overrideval; + }; + struct // ID_ALIAS + { + uint *code; + identval val; + identstack *stack; + }; + struct // ID_COMMAND + { + const char *args; + uint argmask; + }; + }; + identfun fun; // ID_VAR, ID_FVAR, ID_SVAR, ID_COMMAND + + ident() {} + // ID_VAR + ident(int t, const char *n, int m, int x, int *s, void *f = NULL, int flags = 0) + : type(t), flags(flags | (m > x ? IDF_READONLY : 0)), name(n), minval(m), maxval(x), fun((identfun)f) + { storage.i = s; } + // ID_FVAR + ident(int t, const char *n, float m, float x, float *s, void *f = NULL, int flags = 0) + : type(t), flags(flags | (m > x ? IDF_READONLY : 0)), name(n), minvalf(m), maxvalf(x), fun((identfun)f) + { storage.f = s; } + // ID_SVAR + ident(int t, const char *n, char **s, void *f = NULL, int flags = 0) + : type(t), flags(flags), name(n), fun((identfun)f) + { storage.s = s; } + // ID_ALIAS + ident(int t, const char *n, char *a, int flags) + : type(t), valtype(VAL_STR), flags(flags), name(n), code(NULL), stack(NULL) + { val.s = a; } + ident(int t, const char *n, int a, int flags) + : type(t), valtype(VAL_INT), flags(flags), name(n), code(NULL), stack(NULL) + { val.i = a; } + ident(int t, const char *n, float a, int flags) + : type(t), valtype(VAL_FLOAT), flags(flags), name(n), code(NULL), stack(NULL) + { val.f = a; } + ident(int t, const char *n, int flags) + : type(t), valtype(VAL_NULL), flags(flags), name(n), code(NULL), stack(NULL) + {} + ident(int t, const char *n, const tagval &v, int flags) + : type(t), valtype(v.type), flags(flags), name(n), code(NULL), stack(NULL) + { val = v; } + // ID_COMMAND + ident(int t, const char *n, const char *args, uint argmask, int numargs, void *f = NULL, int flags = 0) + : type(t), numargs(numargs), flags(flags), name(n), args(args), argmask(argmask), fun((identfun)f) + {} + + void changed() { if(fun) fun(); } + + void setval(const tagval &v) + { + valtype = v.type; + val = v; + } + + void setval(const identstack &v) + { + valtype = v.valtype; + val = v.val; + } + + void forcenull() + { + if(valtype==VAL_STR) delete[] val.s; + valtype = VAL_NULL; + } + + float getfloat() const; + int getint() const; + const char *getstr() const; + void getval(tagval &v) const; +}; + +extern void addident(ident *id); + +extern tagval *commandret; +extern const char *intstr(int v); +extern void intret(int v); +extern const char *floatstr(float v); +extern void floatret(float v); +extern void stringret(char *s); +extern void result(tagval &v); +extern void result(const char *s); + +static inline int parseint(const char *s) +{ + return int(strtoul(s, NULL, 0)); +} + +static inline float parsefloat(const char *s) +{ + // not all platforms (windows) can parse hexadecimal integers via strtod + char *end; + double val = strtod(s, &end); + return val || end==s || (*end!='x' && *end!='X') ? float(val) : float(parseint(s)); +} + +static inline void intformat(char *buf, int v, int len = 20) { nformatstring(buf, len, "%d", v); } +static inline void floatformat(char *buf, float v, int len = 20) { nformatstring(buf, len, v==int(v) ? "%.1f" : "%.6g", v); } + +static inline const char *getstr(const identval &v, int type) +{ + switch(type) + { + case VAL_STR: case VAL_MACRO: return v.s; + case VAL_INT: return intstr(v.i); + case VAL_FLOAT: return floatstr(v.f); + default: return ""; + } +} +inline const char *tagval::getstr() const { return ::getstr(*this, type); } +inline const char *ident::getstr() const { return ::getstr(val, valtype); } + +static inline int getint(const identval &v, int type) +{ + switch(type) + { + case VAL_INT: return v.i; + case VAL_FLOAT: return int(v.f); + case VAL_STR: case VAL_MACRO: return parseint(v.s); + default: return 0; + } +} +inline int tagval::getint() const { return ::getint(*this, type); } +inline int ident::getint() const { return ::getint(val, valtype); } + +static inline float getfloat(const identval &v, int type) +{ + switch(type) + { + case VAL_FLOAT: return v.f; + case VAL_INT: return float(v.i); + case VAL_STR: case VAL_MACRO: return parsefloat(v.s); + default: return 0.0f; + } +} +inline float tagval::getfloat() const { return ::getfloat(*this, type); } +inline float ident::getfloat() const { return ::getfloat(val, valtype); } + +inline void ident::getval(tagval &v) const +{ + switch(valtype) + { + case VAL_STR: case VAL_MACRO: v.setstr(newstring(val.s)); break; + case VAL_INT: v.setint(val.i); break; + case VAL_FLOAT: v.setfloat(val.f); break; + default: v.setnull(); break; + } +} + +// nasty macros for registering script functions, abuses globals to avoid excessive infrastructure +#define KEYWORD(name, type) UNUSED static bool __dummy_##name = addkeyword(type, #name) +#define COMMANDN(name, fun, nargs) UNUSED static bool __dummy_##fun = addcommand(#name, (identfun)fun, nargs) +#define COMMAND(name, nargs) COMMANDN(name, name, nargs) + +#define _VAR(name, global, min, cur, max, persist) int global = variable(#name, min, cur, max, &global, NULL, persist) +#define VARN(name, global, min, cur, max) _VAR(name, global, min, cur, max, 0) +#define VARNP(name, global, min, cur, max) _VAR(name, global, min, cur, max, IDF_PERSIST) +#define VARNR(name, global, min, cur, max) _VAR(name, global, min, cur, max, IDF_OVERRIDE) +#define VAR(name, min, cur, max) _VAR(name, name, min, cur, max, 0) +#define VARP(name, min, cur, max) _VAR(name, name, min, cur, max, IDF_PERSIST) +#define VARR(name, min, cur, max) _VAR(name, name, min, cur, max, IDF_OVERRIDE) +#define _VARF(name, global, min, cur, max, body, persist) void var_##name(); int global = variable(#name, min, cur, max, &global, var_##name, persist); void var_##name() { body; } +#define VARFN(name, global, min, cur, max, body) _VARF(name, global, min, cur, max, body, 0) +#define VARF(name, min, cur, max, body) _VARF(name, name, min, cur, max, body, 0) +#define VARFP(name, min, cur, max, body) _VARF(name, name, min, cur, max, body, IDF_PERSIST) +#define VARFR(name, min, cur, max, body) _VARF(name, name, min, cur, max, body, IDF_OVERRIDE) +#define VARFNP(name, global, min, cur, max, body) _VARF(name, global, min, cur, max, body, IDF_PERSIST) + +#define _HVAR(name, global, min, cur, max, persist) int global = variable(#name, min, cur, max, &global, NULL, persist | IDF_HEX) +#define HVARN(name, global, min, cur, max) _HVAR(name, global, min, cur, max, 0) +#define HVARNP(name, global, min, cur, max) _HVAR(name, global, min, cur, max, IDF_PERSIST) +#define HVARNR(name, global, min, cur, max) _HVAR(name, global, min, cur, max, IDF_OVERRIDE) +#define HVAR(name, min, cur, max) _HVAR(name, name, min, cur, max, 0) +#define HVARP(name, min, cur, max) _HVAR(name, name, min, cur, max, IDF_PERSIST) +#define HVARR(name, min, cur, max) _HVAR(name, name, min, cur, max, IDF_OVERRIDE) +#define _HVARF(name, global, min, cur, max, body, persist) void var_##name(); int global = variable(#name, min, cur, max, &global, var_##name, persist | IDF_HEX); void var_##name() { body; } +#define HVARFN(name, global, min, cur, max, body) _HVARF(name, global, min, cur, max, body, 0) +#define HVARF(name, min, cur, max, body) _HVARF(name, name, min, cur, max, body, 0) +#define HVARFP(name, min, cur, max, body) _HVARF(name, name, min, cur, max, body, IDF_PERSIST) +#define HVARFR(name, min, cur, max, body) _HVARF(name, name, min, cur, max, body, IDF_OVERRIDE) + +#define _FVAR(name, global, min, cur, max, persist) float global = fvariable(#name, min, cur, max, &global, NULL, persist) +#define FVARN(name, global, min, cur, max) _FVAR(name, global, min, cur, max, 0) +#define FVARNP(name, global, min, cur, max) _FVAR(name, global, min, cur, max, IDF_PERSIST) +#define FVARNR(name, global, min, cur, max) _FVAR(name, global, min, cur, max, IDF_OVERRIDE) +#define FVAR(name, min, cur, max) _FVAR(name, name, min, cur, max, 0) +#define FVARP(name, min, cur, max) _FVAR(name, name, min, cur, max, IDF_PERSIST) +#define FVARR(name, min, cur, max) _FVAR(name, name, min, cur, max, IDF_OVERRIDE) +#define _FVARF(name, global, min, cur, max, body, persist) void var_##name(); float global = fvariable(#name, min, cur, max, &global, var_##name, persist); void var_##name() { body; } +#define FVARFN(name, global, min, cur, max, body) _FVARF(name, global, min, cur, max, body, 0) +#define FVARF(name, min, cur, max, body) _FVARF(name, name, min, cur, max, body, 0) +#define FVARFP(name, min, cur, max, body) _FVARF(name, name, min, cur, max, body, IDF_PERSIST) +#define FVARFR(name, min, cur, max, body) _FVARF(name, name, min, cur, max, body, IDF_OVERRIDE) + +#define _SVAR(name, global, cur, persist) char *global = svariable(#name, cur, &global, NULL, persist) +#define SVARN(name, global, cur) _SVAR(name, global, cur, 0) +#define SVARNP(name, global, cur) _SVAR(name, global, cur, IDF_PERSIST) +#define SVARNR(name, global, cur) _SVAR(name, global, cur, IDF_OVERRIDE) +#define SVAR(name, cur) _SVAR(name, name, cur, 0) +#define SVARP(name, cur) _SVAR(name, name, cur, IDF_PERSIST) +#define SVARR(name, cur) _SVAR(name, name, cur, IDF_OVERRIDE) +#define _SVARF(name, global, cur, body, persist) void var_##name(); char *global = svariable(#name, cur, &global, var_##name, persist); void var_##name() { body; } +#define SVARFN(name, global, cur, body) _SVARF(name, global, cur, body, 0) +#define SVARF(name, cur, body) _SVARF(name, name, cur, body, 0) +#define SVARFP(name, cur, body) _SVARF(name, name, cur, body, IDF_PERSIST) +#define SVARFR(name, cur, body) _SVARF(name, name, cur, body, IDF_OVERRIDE) + +// anonymous inline commands, uses nasty template trick with line numbers to keep names unique +#define ICOMMANDNS(name, cmdname, nargs, proto, b) template<int N> struct cmdname; template<> struct cmdname<__LINE__> { static bool init; static void run proto; }; bool cmdname<__LINE__>::init = addcommand(name, (identfun)cmdname<__LINE__>::run, nargs); void cmdname<__LINE__>::run proto \ + { b; } +#define ICOMMANDN(name, cmdname, nargs, proto, b) ICOMMANDNS(#name, cmdname, nargs, proto, b) +#define ICOMMANDNAME(name) _icmd_##name +#define ICOMMAND(name, nargs, proto, b) ICOMMANDN(name, ICOMMANDNAME(name), nargs, proto, b) +#define ICOMMANDSNAME _icmds_ +#define ICOMMANDS(name, nargs, proto, b) ICOMMANDNS(name, ICOMMANDSNAME, nargs, proto, b) + diff --git a/src/shared/crypto.cpp b/src/shared/crypto.cpp new file mode 100644 index 0000000..134afc5 --- /dev/null +++ b/src/shared/crypto.cpp @@ -0,0 +1,944 @@ +#include "cube.h" + +///////////////////////// cryptography ///////////////////////////////// + +/* Based off the reference implementation of Tiger, a cryptographically + * secure 192 bit hash function by Ross Anderson and Eli Biham. More info at: + * http://www.cs.technion.ac.il/~biham/Reports/Tiger/ + */ + +#define TIGER_PASSES 3 + +namespace tiger +{ + typedef unsigned long long int chunk; + + union hashval + { + uchar bytes[3*8]; + chunk chunks[3]; + }; + + chunk sboxes[4*256]; + + void compress(const chunk *str, chunk state[3]) + { + chunk a, b, c; + chunk aa, bb, cc; + chunk x0, x1, x2, x3, x4, x5, x6, x7; + + a = state[0]; + b = state[1]; + c = state[2]; + + x0=str[0]; x1=str[1]; x2=str[2]; x3=str[3]; + x4=str[4]; x5=str[5]; x6=str[6]; x7=str[7]; + + aa = a; + bb = b; + cc = c; + + loop(pass_no, TIGER_PASSES) + { + if(pass_no) + { + x0 -= x7 ^ 0xA5A5A5A5A5A5A5A5ULL; x1 ^= x0; x2 += x1; x3 -= x2 ^ ((~x1)<<19); + x4 ^= x3; x5 += x4; x6 -= x5 ^ ((~x4)>>23); x7 ^= x6; + x0 += x7; x1 -= x0 ^ ((~x7)<<19); x2 ^= x1; x3 += x2; + x4 -= x3 ^ ((~x2)>>23); x5 ^= x4; x6 += x5; x7 -= x6 ^ 0x0123456789ABCDEFULL; + } + +#define sb1 (sboxes) +#define sb2 (sboxes+256) +#define sb3 (sboxes+256*2) +#define sb4 (sboxes+256*3) + +#define round(a, b, c, x) \ + c ^= x; \ + a -= sb1[((c)>>(0*8))&0xFF] ^ sb2[((c)>>(2*8))&0xFF] ^ \ + sb3[((c)>>(4*8))&0xFF] ^ sb4[((c)>>(6*8))&0xFF] ; \ + b += sb4[((c)>>(1*8))&0xFF] ^ sb3[((c)>>(3*8))&0xFF] ^ \ + sb2[((c)>>(5*8))&0xFF] ^ sb1[((c)>>(7*8))&0xFF] ; \ + b *= mul; + + uint mul = !pass_no ? 5 : (pass_no==1 ? 7 : 9); + round(a, b, c, x0) round(b, c, a, x1) round(c, a, b, x2) round(a, b, c, x3) + round(b, c, a, x4) round(c, a, b, x5) round(a, b, c, x6) round(b, c, a, x7) + + chunk tmp = a; a = c; c = b; b = tmp; + } + + a ^= aa; + b -= bb; + c += cc; + + state[0] = a; + state[1] = b; + state[2] = c; + } + + void gensboxes() + { + const char *str = "Tiger - A Fast New Hash Function, by Ross Anderson and Eli Biham"; + chunk state[3] = { 0x0123456789ABCDEFULL, 0xFEDCBA9876543210ULL, 0xF096A5B4C3B2E187ULL }; + uchar temp[64]; + + if(!*(const uchar *)&islittleendian) loopj(64) temp[j^7] = str[j]; + else loopj(64) temp[j] = str[j]; + loopi(1024) loop(col, 8) ((uchar *)&sboxes[i])[col] = i&0xFF; + + int abc = 2; + loop(pass, 5) loopi(256) for(int sb = 0; sb < 1024; sb += 256) + { + abc++; + if(abc >= 3) { abc = 0; compress((chunk *)temp, state); } + loop(col, 8) + { + uchar val = ((uchar *)&sboxes[sb+i])[col]; + ((uchar *)&sboxes[sb+i])[col] = ((uchar *)&sboxes[sb + ((uchar *)&state[abc])[col]])[col]; + ((uchar *)&sboxes[sb + ((uchar *)&state[abc])[col]])[col] = val; + } + } + } + + void hash(const uchar *str, int length, hashval &val) + { + static bool init = false; + if(!init) { gensboxes(); init = true; } + + uchar temp[64]; + + val.chunks[0] = 0x0123456789ABCDEFULL; + val.chunks[1] = 0xFEDCBA9876543210ULL; + val.chunks[2] = 0xF096A5B4C3B2E187ULL; + + int i = length; + for(; i >= 64; i -= 64, str += 64) + { + if(!*(const uchar *)&islittleendian) + { + loopj(64) temp[j^7] = str[j]; + compress((chunk *)temp, val.chunks); + } + else compress((chunk *)str, val.chunks); + } + + int j; + if(!*(const uchar *)&islittleendian) + { + for(j = 0; j < i; j++) temp[j^7] = str[j]; + temp[j^7] = 0x01; + while(++j&7) temp[j^7] = 0; + } + else + { + for(j = 0; j < i; j++) temp[j] = str[j]; + temp[j] = 0x01; + while(++j&7) temp[j] = 0; + } + + if(j > 56) + { + while(j < 64) temp[j++] = 0; + compress((chunk *)temp, val.chunks); + j = 0; + } + while(j < 56) temp[j++] = 0; + *(chunk *)(temp+56) = (chunk)length<<3; + compress((chunk *)temp, val.chunks); + if(!*(const uchar *)&islittleendian) + { + loopk(3) + { + uchar *c = &val.bytes[k*sizeof(chunk)]; + loopl(sizeof(chunk)/2) swap(c[l], c[sizeof(chunk)-1-l]); + } + } + } +} + +/* Elliptic curve cryptography based on NIST DSS prime curves. */ + +#define BI_DIGIT_BITS 16 +#define BI_DIGIT_MASK ((1<<BI_DIGIT_BITS)-1) + +template<int BI_DIGITS> struct bigint +{ + typedef ushort digit; + typedef uint dbldigit; + + int len; + digit digits[BI_DIGITS]; + + bigint() {} + bigint(digit n) { if(n) { len = 1; digits[0] = n; } else len = 0; } + bigint(const char *s) { parse(s); } + template<int Y_DIGITS> bigint(const bigint<Y_DIGITS> &y) { *this = y; } + + static int parsedigits(ushort *digits, int maxlen, const char *s) + { + int slen = 0; + while(isxdigit(s[slen])) slen++; + int len = (slen+2*sizeof(ushort)-1)/(2*sizeof(ushort)); + if(len>maxlen) return 0; + memset(digits, 0, len*sizeof(ushort)); + loopi(slen) + { + int c = s[slen-i-1]; + if(isalpha(c)) c = toupper(c) - 'A' + 10; + else if(isdigit(c)) c -= '0'; + else return 0; + digits[i/(2*sizeof(ushort))] |= c<<(4*(i%(2*sizeof(ushort)))); + } + return len; + } + + void parse(const char *s) + { + len = parsedigits(digits, BI_DIGITS, s); + shrink(); + } + + void zero() { len = 0; } + + void print(stream *out) const + { + vector<char> buf; + printdigits(buf); + out->write(buf.getbuf(), buf.length()); + } + + void printdigits(vector<char> &buf) const + { + loopi(len) + { + digit d = digits[len-i-1]; + loopj(BI_DIGIT_BITS/4) + { + uint shift = BI_DIGIT_BITS - (j+1)*4; + int val = (d >> shift) & 0xF; + if(val < 10) buf.add('0' + val); + else buf.add('a' + val - 10); + } + } + } + + template<int Y_DIGITS> bigint &operator=(const bigint<Y_DIGITS> &y) + { + len = y.len; + memcpy(digits, y.digits, len*sizeof(digit)); + return *this; + } + + bool iszero() const { return !len; } + bool isone() const { return len==1 && digits[0]==1; } + + int numbits() const + { + if(!len) return 0; + int bits = len*BI_DIGIT_BITS; + digit last = digits[len-1], mask = 1<<(BI_DIGIT_BITS-1); + while(mask) + { + if(last&mask) return bits; + bits--; + mask >>= 1; + } + return 0; + } + + bool hasbit(int n) const { return n/BI_DIGIT_BITS < len && ((digits[n/BI_DIGIT_BITS]>>(n%BI_DIGIT_BITS))&1); } + + bool morebits(int n) const { return len > n/BI_DIGIT_BITS; } + + template<int X_DIGITS, int Y_DIGITS> bigint &add(const bigint<X_DIGITS> &x, const bigint<Y_DIGITS> &y) + { + dbldigit carry = 0; + int maxlen = max(x.len, y.len), i; + for(i = 0; i < y.len || carry; i++) + { + carry += (i < x.len ? (dbldigit)x.digits[i] : 0) + (i < y.len ? (dbldigit)y.digits[i] : 0); + digits[i] = (digit)carry; + carry >>= BI_DIGIT_BITS; + } + if(i < x.len && this != &x) memcpy(&digits[i], &x.digits[i], (x.len - i)*sizeof(digit)); + len = max(i, maxlen); + return *this; + } + template<int Y_DIGITS> bigint &add(const bigint<Y_DIGITS> &y) { return add(*this, y); } + + template<int X_DIGITS, int Y_DIGITS> bigint &sub(const bigint<X_DIGITS> &x, const bigint<Y_DIGITS> &y) + { + ASSERT(x >= y); + dbldigit borrow = 0; + int i; + for(i = 0; i < y.len || borrow; i++) + { + borrow = (1<<BI_DIGIT_BITS) + (dbldigit)x.digits[i] - (i<y.len ? (dbldigit)y.digits[i] : 0) - borrow; + digits[i] = (digit)borrow; + borrow = (borrow>>BI_DIGIT_BITS)^1; + } + if(i < x.len && this != &x) memcpy(&digits[i], &x.digits[i], (x.len - i)*sizeof(digit)); + len = x.len; + shrink(); + return *this; + } + template<int Y_DIGITS> bigint &sub(const bigint<Y_DIGITS> &y) { return sub(*this, y); } + + void shrink() { while(len > 0 && !digits[len-1]) len--; } + void shrinkdigits(int n) { len = n; shrink(); } + void shrinkbits(int n) { shrinkdigits(n/BI_DIGIT_BITS); } + + template<int Y_DIGITS> void copyshrinkdigits(const bigint<Y_DIGITS> &y, int n) + { + len = clamp(y.len, 0, n); + memcpy(digits, y.digits, len*sizeof(digit)); + shrink(); + } + template<int Y_DIGITS> void copyshrinkbits(const bigint<Y_DIGITS> &y, int n) + { + copyshrinkdigits(y, n/BI_DIGIT_BITS); + } + + template<int X_DIGITS, int Y_DIGITS> bigint &mul(const bigint<X_DIGITS> &x, const bigint<Y_DIGITS> &y) + { + if(!x.len || !y.len) { len = 0; return *this; } + memset(digits, 0, y.len*sizeof(digit)); + loopi(x.len) + { + dbldigit carry = 0; + loopj(y.len) + { + carry += (dbldigit)x.digits[i] * (dbldigit)y.digits[j] + (dbldigit)digits[i+j]; + digits[i+j] = (digit)carry; + carry >>= BI_DIGIT_BITS; + } + digits[i+y.len] = carry; + } + len = x.len + y.len; + shrink(); + return *this; + } + + bigint &rshift(int n) + { + assert(len <= BI_DIGITS); + if(!len || n<=0) return *this; + if(n >= len*BI_DIGIT_BITS) { len = 0; return *this; } + int dig = (n-1)/BI_DIGIT_BITS; + n = ((n-1) % BI_DIGIT_BITS)+1; + digit carry = digit(digits[dig]>>n); + for(int i = dig+1; i < len; i++) + { + digit tmp = digits[i]; + digits[i-dig-1] = digit((tmp<<(BI_DIGIT_BITS-n)) | carry); + carry = digit(tmp>>n); + } + digits[len-dig-1] = carry; + len -= dig + (n/BI_DIGIT_BITS); + shrink(); + return *this; + } + + bigint &lshift(int n) + { + if(!len || n<=0) return *this; + int dig = n/BI_DIGIT_BITS; + n %= BI_DIGIT_BITS; + digit carry = 0; + loopirev(len) + { + digit tmp = digits[i]; + digits[i+dig] = digit((tmp<<n) | carry); + carry = digit(tmp>>(BI_DIGIT_BITS-n)); + } + len += dig; + if(carry) digits[len++] = carry; + if(dig) memset(digits, 0, dig*sizeof(digit)); + return *this; + } + + void zerodigits(int i, int n) + { + memset(&digits[i], 0, n*sizeof(digit)); + } + void zerobits(int i, int n) + { + zerodigits(i/BI_DIGIT_BITS, n/BI_DIGIT_BITS); + } + + template<int Y_DIGITS> void copydigits(int to, const bigint<Y_DIGITS> &y, int from, int n) + { + int avail = clamp(y.len-from, 0, n); + memcpy(&digits[to], &y.digits[from], avail*sizeof(digit)); + if(avail < n) memset(&digits[to+avail], 0, (n-avail)*sizeof(digit)); + } + template<int Y_DIGITS> void copybits(int to, const bigint<Y_DIGITS> &y, int from, int n) + { + copydigits(to/BI_DIGIT_BITS, y, from/BI_DIGIT_BITS, n/BI_DIGIT_BITS); + } + + void dupdigits(int to, int from, int n) + { + memcpy(&digits[to], &digits[from], n*sizeof(digit)); + } + void dupbits(int to, int from, int n) + { + dupdigits(to/BI_DIGIT_BITS, from/BI_DIGIT_BITS, n/BI_DIGIT_BITS); + } + + template<int Y_DIGITS> bool operator==(const bigint<Y_DIGITS> &y) const + { + if(len!=y.len) return false; + loopirev(len) if(digits[i]!=y.digits[i]) return false; + return true; + } + template<int Y_DIGITS> bool operator!=(const bigint<Y_DIGITS> &y) const { return !(*this==y); } + template<int Y_DIGITS> bool operator<(const bigint<Y_DIGITS> &y) const + { + if(len<y.len) return true; + if(len>y.len) return false; + loopirev(len) + { + if(digits[i]<y.digits[i]) return true; + if(digits[i]>y.digits[i]) return false; + } + return false; + } + template<int Y_DIGITS> bool operator>(const bigint<Y_DIGITS> &y) const { return y<*this; } + template<int Y_DIGITS> bool operator<=(const bigint<Y_DIGITS> &y) const { return !(y<*this); } + template<int Y_DIGITS> bool operator>=(const bigint<Y_DIGITS> &y) const { return !(*this<y); } +}; + +#define GF_BITS 192 +#define GF_DIGITS ((GF_BITS+BI_DIGIT_BITS-1)/BI_DIGIT_BITS) + +typedef bigint<GF_DIGITS+1> gfint; + +/* NIST prime Galois fields. + * Currently only supports NIST P-192, where P=2^192-2^64-1, and P-256, where P=2^256-2^224+2^192+2^96-1. + */ +struct gfield : gfint +{ + static const gfield P; + + gfield() {} + gfield(digit n) : gfint(n) {} + gfield(const char *s) : gfint(s) {} + + template<int Y_DIGITS> gfield(const bigint<Y_DIGITS> &y) : gfint(y) {} + + template<int Y_DIGITS> gfield &operator=(const bigint<Y_DIGITS> &y) + { + gfint::operator=(y); + return *this; + } + + template<int X_DIGITS, int Y_DIGITS> gfield &add(const bigint<X_DIGITS> &x, const bigint<Y_DIGITS> &y) + { + gfint::add(x, y); + if(*this >= P) gfint::sub(*this, P); + return *this; + } + template<int Y_DIGITS> gfield &add(const bigint<Y_DIGITS> &y) { return add(*this, y); } + + template<int X_DIGITS> gfield &mul2(const bigint<X_DIGITS> &x) { return add(x, x); } + gfield &mul2() { return mul2(*this); } + + gfield &div2() + { + if(hasbit(0)) gfint::add(*this, P); + rshift(1); + return *this; + } + + template<int X_DIGITS, int Y_DIGITS> gfield &sub(const bigint<X_DIGITS> &x, const bigint<Y_DIGITS> &y) + { + if(x < y) + { + gfint tmp; /* necessary if this==&y, using this instead would clobber y */ + tmp.add(x, P); + gfint::sub(tmp, y); + } + else gfint::sub(x, y); + return *this; + } + template<int Y_DIGITS> gfield &sub(const bigint<Y_DIGITS> &y) { return sub(*this, y); } + + template<int X_DIGITS> gfield &neg(const bigint<X_DIGITS> &x) + { + gfint::sub(P, x); + return *this; + } + gfield &neg() { return neg(*this); } + + template<int X_DIGITS> gfield &square(const bigint<X_DIGITS> &x) { return mul(x, x); } + gfield &square() { return square(*this); } + + template<int X_DIGITS, int Y_DIGITS> gfield &mul(const bigint<X_DIGITS> &x, const bigint<Y_DIGITS> &y) + { + bigint<X_DIGITS+Y_DIGITS> result; + result.mul(x, y); + reduce(result); + return *this; + } + template<int Y_DIGITS> gfield &mul(const bigint<Y_DIGITS> &y) { return mul(*this, y); } + + template<int RESULT_DIGITS> void reduce(const bigint<RESULT_DIGITS> &result) + { +#if GF_BITS==192 + // B = T + S1 + S2 + S3 mod p + copyshrinkdigits(result, GF_DIGITS); // T + + if(result.morebits(192)) + { + gfield s; + s.copybits(0, result, 192, 64); + s.dupbits(64, 0, 64); + s.shrinkbits(128); + add(s); // S1 + + if(result.morebits(256)) + { + s.zerobits(0, 64); + s.copybits(64, result, 256, 64); + s.dupbits(128, 64, 64); + s.shrinkdigits(GF_DIGITS); + add(s); // S2 + + if(result.morebits(320)) + { + s.copybits(0, result, 320, 64); + s.dupbits(64, 0, 64); + s.dupbits(128, 0, 64); + s.shrinkdigits(GF_DIGITS); + add(s); // S3 + } + } + } + else if(*this >= P) gfint::sub(*this, P); +#elif GF_BITS==256 + // B = T + 2*S1 + 2*S2 + S3 + S4 - D1 - D2 - D3 - D4 mod p + copyshrinkdigits(result, GF_DIGITS); // T + + if(result.morebits(256)) + { + gfield s; + if(result.morebits(352)) + { + s.zerobits(0, 96); + s.copybits(96, result, 352, 160); + s.shrinkdigits(GF_DIGITS); + add(s); add(s); // S1 + + if(result.morebits(384)) + { + //s.zerobits(0, 96); + s.copybits(96, result, 384, 128); + s.shrinkbits(224); + add(s); add(s); // S2 + } + } + + s.copybits(0, result, 256, 96); + s.zerobits(96, 96); + s.copybits(192, result, 448, 64); + s.shrinkdigits(GF_DIGITS); + add(s); // S3 + + s.copybits(0, result, 288, 96); + s.copybits(96, result, 416, 96); + s.dupbits(192, 96, 32); + s.copybits(224, result, 256, 32); + s.shrinkdigits(GF_DIGITS); + add(s); // S4 + + s.copybits(0, result, 352, 96); + s.zerobits(96, 96); + s.copybits(192, result, 256, 32); + s.copybits(224, result, 320, 32); + s.shrinkdigits(GF_DIGITS); + sub(s); // D1 + + s.copybits(0, result, 384, 128); + //s.zerobits(128, 64); + s.copybits(192, result, 288, 32); + s.copybits(224, result, 352, 32); + s.shrinkdigits(GF_DIGITS); + sub(s); // D2 + + s.copybits(0, result, 416, 96); + s.copybits(96, result, 256, 96); + s.zerobits(192, 32); + s.copybits(224, result, 384, 32); + s.shrinkdigits(GF_DIGITS); + sub(s); // D3 + + s.copybits(0, result, 448, 64); + s.zerobits(64, 32); + s.copybits(96, result, 288, 96); + //s.zerobits(192, 32); + s.copybits(224, result, 416, 32); + s.shrinkdigits(GF_DIGITS); + sub(s); // D4 + } + else if(*this >= P) gfint::sub(*this, P); +#else +#error Unsupported GF +#endif + } + + template<int X_DIGITS, int Y_DIGITS> gfield &pow(const bigint<X_DIGITS> &x, const bigint<Y_DIGITS> &y) + { + gfield a(x); + if(y.hasbit(0)) *this = a; + else + { + len = 1; + digits[0] = 1; + if(!y.len) return *this; + } + for(int i = 1, j = y.numbits(); i < j; i++) + { + a.square(); + if(y.hasbit(i)) mul(a); + } + return *this; + } + template<int Y_DIGITS> gfield &pow(const bigint<Y_DIGITS> &y) { return pow(*this, y); } + + bool invert(const gfield &x) + { + if(!x.len) return false; + gfint u(x), v(P), A((gfint::digit)1), C((gfint::digit)0); + while(!u.iszero()) + { + int ushift = 0, ashift = 0; + while(!u.hasbit(ushift)) + { + ushift++; + if(A.hasbit(ashift)) + { + if(ashift) { A.rshift(ashift); ashift = 0; } + A.add(P); + } + ashift++; + } + if(ushift) u.rshift(ushift); + if(ashift) A.rshift(ashift); + int vshift = 0, cshift = 0; + while(!v.hasbit(vshift)) + { + vshift++; + if(C.hasbit(cshift)) + { + if(cshift) { C.rshift(cshift); cshift = 0; } + C.add(P); + } + cshift++; + } + if(vshift) v.rshift(vshift); + if(cshift) C.rshift(cshift); + if(u >= v) + { + u.sub(v); + if(A < C) A.add(P); + A.sub(C); + } + else + { + v.sub(v, u); + if(C < A) C.add(P); + C.sub(A); + } + } + if(C >= P) gfint::sub(C, P); + else { len = C.len; memcpy(digits, C.digits, len*sizeof(digit)); } + ASSERT(*this < P); + return true; + } + void invert() { invert(*this); } + + template<int X_DIGITS> static int legendre(const bigint<X_DIGITS> &x) + { + static const gfint Psub1div2(gfint(P).sub(bigint<1>(1)).rshift(1)); + gfield L; + L.pow(x, Psub1div2); + if(!L.len) return 0; + if(L.len==1) return 1; + return -1; + } + int legendre() const { return legendre(*this); } + + bool sqrt(const gfield &x) + { + if(!x.len) { len = 0; return true; } +#if GF_BITS==224 +#error Unsupported GF +#else + ASSERT((P.digits[0]%4)==3); + static const gfint Padd1div4(gfint(P).add(bigint<1>(1)).rshift(2)); + switch(legendre(x)) + { + case 0: len = 0; return true; + case -1: return false; + default: pow(x, Padd1div4); return true; + } +#endif + } + bool sqrt() { return sqrt(*this); } +}; + +struct ecjacobian +{ + static const gfield B; + static const ecjacobian base; + static const ecjacobian origin; + + gfield x, y, z; + + ecjacobian() {} + ecjacobian(const gfield &x, const gfield &y) : x(x), y(y), z(bigint<1>(1)) {} + ecjacobian(const gfield &x, const gfield &y, const gfield &z) : x(x), y(y), z(z) {} + + void mul2() + { + if(z.iszero()) return; + else if(y.iszero()) { *this = origin; return; } + gfield a, b, c, d; + d.sub(x, c.square(z)); + d.mul(c.add(x)); + c.mul2(d).add(d); + z.mul(y).add(z); + a.square(y); + b.mul2(a); + d.mul2(x).mul(b); + x.square(c).sub(d).sub(d); + a.square(b).add(a); + y.sub(d, x).mul(c).sub(a); + } + + void add(const ecjacobian &q) + { + if(q.z.iszero()) return; + else if(z.iszero()) { *this = q; return; } + gfield a, b, c, d, e, f; + a.square(z); + b.mul(q.y, a).mul(z); + a.mul(q.x); + if(q.z.isone()) + { + c.add(x, a); + d.add(y, b); + a.sub(x, a); + b.sub(y, b); + } + else + { + f.mul(y, e.square(q.z)).mul(q.z); + e.mul(x); + c.add(e, a); + d.add(f, b); + a.sub(e, a); + b.sub(f, b); + } + if(a.iszero()) { if(b.iszero()) mul2(); else *this = origin; return; } + if(!q.z.isone()) z.mul(q.z); + z.mul(a); + x.square(b).sub(f.mul(c, e.square(a))); + y.sub(f, x).sub(x).mul(b).sub(e.mul(a).mul(d)).div2(); + } + + template<int Q_DIGITS> void mul(const ecjacobian &p, const bigint<Q_DIGITS> &q) + { + *this = origin; + loopirev(q.numbits()) + { + mul2(); + if(q.hasbit(i)) add(p); + } + } + template<int Q_DIGITS> void mul(const bigint<Q_DIGITS> &q) { ecjacobian tmp(*this); mul(tmp, q); } + + void normalize() + { + if(z.iszero() || z.isone()) return; + gfield tmp; + z.invert(); + tmp.square(z); + x.mul(tmp); + y.mul(tmp).mul(z); + z = bigint<1>(1); + } + + bool calcy(bool ybit) + { + gfield y2, tmp; + y2.square(x).mul(x).sub(tmp.add(x, x).add(x)).add(B); + if(!y.sqrt(y2)) { y.zero(); return false; } + if(y.hasbit(0) != ybit) y.neg(); + return true; + } + + void print(vector<char> &buf) + { + normalize(); + buf.add(y.hasbit(0) ? '-' : '+'); + x.printdigits(buf); + } + + void parse(const char *s) + { + bool ybit = *s++ == '-'; + x.parse(s); + calcy(ybit); + z = bigint<1>(1); + } +}; + +const ecjacobian ecjacobian::origin(gfield((gfield::digit)1), gfield((gfield::digit)1), gfield((gfield::digit)0)); + +#if GF_BITS==192 +const gfield gfield::P("fffffffffffffffffffffffffffffffeffffffffffffffff"); +const gfield ecjacobian::B("64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1"); +const ecjacobian ecjacobian::base( + gfield("188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012"), + gfield("07192b95ffc8da78631011ed6b24cdd573f977a11e794811") +); +#elif GF_BITS==224 +const gfield gfield::P("ffffffffffffffffffffffffffffffff000000000000000000000001"); +const gfield ecjacobian::B("b4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4"); +const ecjacobian ecjacobian::base( + gfield("b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21"), + gfield("bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34") +); +#elif GF_BITS==256 +const gfield gfield::P("ffffffff00000001000000000000000000000000ffffffffffffffffffffffff"); +const gfield ecjacobian::B("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b"); +const ecjacobian ecjacobian::base( + gfield("6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296"), + gfield("4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5") +); +#elif GF_BITS==384 +const gfield gfield::P("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff"); +const gfield ecjacobian::B("b3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef"); +const ecjacobian ecjacobian::base( + gfield("aa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7"), + gfield("3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f") +); +#elif GF_BITS==521 +const gfield gfield::P("1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); +const gfield ecjacobian::B("051953eb968e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00"); +const ecjacobian ecjacobian::base( + gfield("c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66"), + gfield("11839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650") +); +#else +#error Unsupported GF +#endif + +void calcpubkey(gfint privkey, vector<char> &pubstr) +{ + ecjacobian c(ecjacobian::base); + c.mul(privkey); + c.normalize(); + c.print(pubstr); + pubstr.add('\0'); +} + +bool calcpubkey(const char *privstr, vector<char> &pubstr) +{ + if(!privstr[0]) return false; + gfint privkey; + privkey.parse(privstr); + calcpubkey(privkey, pubstr); + return true; +} + +void genprivkey(const char *seed, vector<char> &privstr, vector<char> &pubstr) +{ + tiger::hashval hash; + tiger::hash((const uchar *)seed, (int)strlen(seed), hash); + bigint<8*sizeof(hash.bytes)/BI_DIGIT_BITS> privkey; + memcpy(privkey.digits, hash.bytes, sizeof(hash.bytes)); + privkey.len = 8*sizeof(hash.bytes)/BI_DIGIT_BITS; + privkey.shrink(); + privkey.printdigits(privstr); + privstr.add('\0'); + + calcpubkey(privkey, pubstr); +} + +bool hashstring(const char *str, char *result, int maxlen) +{ + tiger::hashval hv; + if(maxlen < 2*(int)sizeof(hv.bytes) + 1) return false; + tiger::hash((uchar *)str, strlen(str), hv); + loopi(sizeof(hv.bytes)) + { + uchar c = hv.bytes[i]; + *result++ = "0123456789abcdef"[c&0xF]; + *result++ = "0123456789abcdef"[c>>4]; + } + *result = '\0'; + return true; +} + +void answerchallenge(const char *privstr, const char *challenge, vector<char> &answerstr) +{ + gfint privkey; + privkey.parse(privstr); + ecjacobian answer; + answer.parse(challenge); + answer.mul(privkey); + answer.normalize(); + answer.x.printdigits(answerstr); + answerstr.add('\0'); +} + +void *parsepubkey(const char *pubstr) +{ + ecjacobian *pubkey = new ecjacobian; + pubkey->parse(pubstr); + return pubkey; +} + +void freepubkey(void *pubkey) +{ + delete (ecjacobian *)pubkey; +} + +void *genchallenge(void *pubkey, const void *seed, int seedlen, vector<char> &challengestr) +{ + tiger::hashval hash; + tiger::hash((const uchar *)seed, seedlen, hash); + gfint challenge; + memcpy(challenge.digits, hash.bytes, sizeof(hash.bytes)); + challenge.len = 8*sizeof(hash.bytes)/BI_DIGIT_BITS; + challenge.shrink(); + + ecjacobian answer(*(ecjacobian *)pubkey); + answer.mul(challenge); + answer.normalize(); + + ecjacobian secret(ecjacobian::base); + secret.mul(challenge); + secret.normalize(); + + secret.print(challengestr); + challengestr.add('\0'); + + return new gfield(answer.x); +} + +void freechallenge(void *answer) +{ + delete (gfint *)answer; +} + +bool checkchallenge(const char *answerstr, void *correct) +{ + gfint answer(answerstr); + return answer == *(gfint *)correct; +} + diff --git a/src/shared/cube.h b/src/shared/cube.h new file mode 100644 index 0000000..ed1c207 --- /dev/null +++ b/src/shared/cube.h @@ -0,0 +1,68 @@ +#ifndef __CUBE_H__ +#define __CUBE_H__ + +#define _FILE_OFFSET_BITS 64 + +#ifdef WIN32 +#define _USE_MATH_DEFINES +#endif +#include <math.h> + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <stdarg.h> +#include <limits.h> +#include <assert.h> +#include <time.h> + +#ifdef WIN32 + #define WIN32_LEAN_AND_MEAN + #ifdef _WIN32_WINNT + #undef _WIN32_WINNT + #endif + #define _WIN32_WINNT 0x0500 + #include "windows.h" + #ifndef _WINDOWS + #define _WINDOWS + #endif + #ifndef __GNUC__ + #include <eh.h> + #include <dbghelp.h> + #include <intrin.h> + #endif + #define ZLIB_DLL +#endif + +#ifndef STANDALONE + #ifdef __APPLE__ + #include "SDL.h" + #define GL_GLEXT_LEGACY + #define __glext_h_ + #include <OpenGL/gl.h> + #else + #include <SDL.h> + #include <SDL_opengl.h> + #endif +#endif + +#include <enet/enet.h> + +#include <zlib.h> + +#include "tools.h" +#include "geom.h" +#include "ents.h" +#include "command.h" + +#ifndef STANDALONE +#include "glexts.h" +#include "glemu.h" +#endif + +#include "iengine.h" +#include "igame.h" + +#endif + diff --git a/src/shared/cube2font.c b/src/shared/cube2font.c new file mode 100644 index 0000000..94e407d --- /dev/null +++ b/src/shared/cube2font.c @@ -0,0 +1,556 @@ +#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <zlib.h>
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_STROKER_H
+#include FT_GLYPH_H
+
+typedef unsigned char uchar;
+typedef unsigned short ushort;
+typedef unsigned int uint;
+
+int imin(int a, int b) { return a < b ? a : b; }
+int imax(int a, int b) { return a > b ? a : b; }
+
+void fatal(const char *fmt, ...)
+{
+ va_list v;
+ va_start(v, fmt);
+ vfprintf(stderr, fmt, v);
+ va_end(v);
+ fputc('\n', stderr);
+
+ exit(EXIT_FAILURE);
+}
+
+uint bigswap(uint n)
+{
+ const int islittleendian = 1;
+ return *(const uchar *)&islittleendian ? (n<<24) | (n>>24) | ((n>>8)&0xFF00) | ((n<<8)&0xFF0000) : n;
+}
+
+size_t writebig(FILE *f, uint n)
+{
+ n = bigswap(n);
+ return fwrite(&n, 1, sizeof(n), f);
+}
+
+void writepngchunk(FILE *f, const char *type, uchar *data, uint len)
+{
+ uint crc;
+ writebig(f, len);
+ fwrite(type, 1, 4, f);
+ fwrite(data, 1, len, f);
+
+ crc = crc32(0, Z_NULL, 0);
+ crc = crc32(crc, (const Bytef *)type, 4);
+ if(data) crc = crc32(crc, data, len);
+ writebig(f, crc);
+}
+
+struct pngihdr
+{
+ uint width, height;
+ uchar bitdepth, colortype, compress, filter, interlace;
+};
+
+void savepng(const char *filename, uchar *data, int w, int h, int bpp, int flip)
+{
+ const uchar signature[] = { 137, 80, 78, 71, 13, 10, 26, 10 };
+ struct pngihdr ihdr;
+ FILE *f;
+ long idat;
+ uint len, crc;
+ z_stream z;
+ uchar buf[1<<12];
+ int i, j;
+
+ memset(&ihdr, 0, sizeof(ihdr));
+ ihdr.width = bigswap(w);
+ ihdr.height = bigswap(h);
+ ihdr.bitdepth = 8;
+ switch(bpp)
+ {
+ case 1: ihdr.colortype = 0; break;
+ case 2: ihdr.colortype = 4; break;
+ case 3: ihdr.colortype = 2; break;
+ case 4: ihdr.colortype = 6; break;
+ default: fatal("cube2font: invalid PNG bpp"); return;
+ }
+ f = fopen(filename, "wb");
+ if(!f) { fatal("cube2font: could not write to %s", filename); return; }
+
+ fwrite(signature, 1, sizeof(signature), f);
+
+ writepngchunk(f, "IHDR", (uchar *)&ihdr, 13);
+
+ idat = ftell(f);
+ len = 0;
+ fwrite("\0\0\0\0IDAT", 1, 8, f);
+ crc = crc32(0, Z_NULL, 0);
+ crc = crc32(crc, (const Bytef *)"IDAT", 4);
+
+ z.zalloc = NULL;
+ z.zfree = NULL;
+ z.opaque = NULL;
+
+ if(deflateInit(&z, Z_BEST_COMPRESSION) != Z_OK)
+ goto error;
+
+ z.next_out = (Bytef *)buf;
+ z.avail_out = sizeof(buf);
+
+ for(i = 0; i < h; i++)
+ {
+ uchar filter = 0;
+ for(j = 0; j < 2; j++)
+ {
+ z.next_in = j ? (Bytef *)data + (flip ? h-i-1 : i)*w*bpp : (Bytef *)&filter;
+ z.avail_in = j ? w*bpp : 1;
+ while(z.avail_in > 0)
+ {
+ if(deflate(&z, Z_NO_FLUSH) != Z_OK) goto cleanuperror;
+ #define FLUSHZ do { \
+ int flush = sizeof(buf) - z.avail_out; \
+ crc = crc32(crc, buf, flush); \
+ len += flush; \
+ fwrite(buf, 1, flush, f); \
+ z.next_out = (Bytef *)buf; \
+ z.avail_out = sizeof(buf); \
+ } while(0)
+ FLUSHZ;
+ }
+ }
+ }
+
+ for(;;)
+ {
+ int err = deflate(&z, Z_FINISH);
+ if(err != Z_OK && err != Z_STREAM_END) goto cleanuperror;
+ FLUSHZ;
+ if(err == Z_STREAM_END) break;
+ }
+
+ deflateEnd(&z);
+
+ fseek(f, idat, SEEK_SET);
+ writebig(f, len);
+ fseek(f, 0, SEEK_END);
+ writebig(f, crc);
+
+ writepngchunk(f, "IEND", NULL, 0);
+
+ fclose(f);
+ return;
+
+cleanuperror:
+ deflateEnd(&z);
+
+error:
+ fclose(f);
+
+ fatal("cube2font: failed saving PNG to %s", filename);
+}
+
+enum
+{
+ CT_PRINT = 1<<0,
+ CT_SPACE = 1<<1,
+ CT_DIGIT = 1<<2,
+ CT_ALPHA = 1<<3,
+ CT_LOWER = 1<<4,
+ CT_UPPER = 1<<5,
+ CT_UNICODE = 1<<6
+};
+#define CUBECTYPE(s, p, d, a, A, u, U) \
+ 0, U, U, U, U, U, U, U, U, s, s, s, s, s, U, U, \
+ U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, \
+ s, p, p, p, p, p, p, p, p, p, p, p, p, p, p, p, \
+ d, d, d, d, d, d, d, d, d, d, p, p, p, p, p, p, \
+ p, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, \
+ A, A, A, A, A, A, A, A, A, A, A, p, p, p, p, p, \
+ p, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, \
+ a, a, a, a, a, a, a, a, a, a, a, p, p, p, p, U, \
+ U, u, u, u, u, u, u, u, u, u, u, u, u, u, u, u, \
+ u, u, u, u, u, u, u, u, u, u, u, u, u, u, u, U, \
+ u, U, u, U, u, U, u, U, u, U, u, U, u, U, u, U, \
+ u, U, u, U, u, U, u, U, u, U, u, U, u, U, u, U, \
+ u, U, u, U, u, U, u, U, U, u, U, u, U, u, U, U, \
+ U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, \
+ U, U, U, U, u, u, u, u, u, u, u, u, u, u, u, u, \
+ u, u, u, u, u, u, u, u, u, u, u, u, u, u, U, u
+const uchar cubectype[256] =
+{
+ CUBECTYPE(CT_SPACE,
+ CT_PRINT,
+ CT_PRINT|CT_DIGIT,
+ CT_PRINT|CT_ALPHA|CT_LOWER,
+ CT_PRINT|CT_ALPHA|CT_UPPER,
+ CT_PRINT|CT_UNICODE|CT_ALPHA|CT_LOWER,
+ CT_PRINT|CT_UNICODE|CT_ALPHA|CT_UPPER)
+};
+int iscubeprint(uchar c) { return cubectype[c]&CT_PRINT; }
+int iscubespace(uchar c) { return cubectype[c]&CT_SPACE; }
+int iscubealpha(uchar c) { return cubectype[c]&CT_ALPHA; }
+int iscubealnum(uchar c) { return cubectype[c]&(CT_ALPHA|CT_DIGIT); }
+int iscubelower(uchar c) { return cubectype[c]&CT_LOWER; }
+int iscubeupper(uchar c) { return cubectype[c]&CT_UPPER; }
+const int cube2unichars[256] =
+{
+ 0, 192, 193, 194, 195, 196, 197, 198, 199, 9, 10, 11, 12, 13, 200, 201,
+ 202, 203, 204, 205, 206, 207, 209, 210, 211, 212, 213, 214, 216, 217, 218, 219,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 220,
+ 221, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237,
+ 238, 239, 241, 242, 243, 244, 245, 246, 248, 249, 250, 251, 252, 253, 255, 0x104,
+ 0x105, 0x106, 0x107, 0x10C, 0x10D, 0x10E, 0x10F, 0x118, 0x119, 0x11A, 0x11B, 0x11E, 0x11F, 0x130, 0x131, 0x141,
+ 0x142, 0x143, 0x144, 0x147, 0x148, 0x150, 0x151, 0x152, 0x153, 0x158, 0x159, 0x15A, 0x15B, 0x15E, 0x15F, 0x160,
+ 0x161, 0x164, 0x165, 0x16E, 0x16F, 0x170, 0x171, 0x178, 0x179, 0x17A, 0x17B, 0x17C, 0x17D, 0x17E, 0x404, 0x411,
+ 0x413, 0x414, 0x416, 0x417, 0x418, 0x419, 0x41B, 0x41F, 0x423, 0x424, 0x426, 0x427, 0x428, 0x429, 0x42A, 0x42B,
+ 0x42C, 0x42D, 0x42E, 0x42F, 0x431, 0x432, 0x433, 0x434, 0x436, 0x437, 0x438, 0x439, 0x43A, 0x43B, 0x43C, 0x43D,
+ 0x43F, 0x442, 0x444, 0x446, 0x447, 0x448, 0x449, 0x44A, 0x44B, 0x44C, 0x44D, 0x44E, 0x44F, 0x454, 0x490, 0x491
+};
+int cube2uni(uchar c)
+{
+ return cube2unichars[c];
+}
+
+const char *encodeutf8(int uni)
+{
+ static char buf[7];
+ char *dst = buf;
+ if(uni <= 0x7F) { *dst++ = uni; goto uni1; }
+ else if(uni <= 0x7FF) { *dst++ = 0xC0 | (uni>>6); goto uni2; }
+ else if(uni <= 0xFFFF) { *dst++ = 0xE0 | (uni>>12); goto uni3; }
+ else if(uni <= 0x1FFFFF) { *dst++ = 0xF0 | (uni>>18); goto uni4; }
+ else if(uni <= 0x3FFFFFF) { *dst++ = 0xF8 | (uni>>24); goto uni5; }
+ else if(uni <= 0x7FFFFFFF) { *dst++ = 0xFC | (uni>>30); goto uni6; }
+ else goto uni1;
+uni6: *dst++ = 0x80 | ((uni>>24)&0x3F);
+uni5: *dst++ = 0x80 | ((uni>>18)&0x3F);
+uni4: *dst++ = 0x80 | ((uni>>12)&0x3F);
+uni3: *dst++ = 0x80 | ((uni>>6)&0x3F);
+uni2: *dst++ = 0x80 | (uni&0x3F);
+uni1: *dst++ = '\0';
+ return buf;
+}
+
+struct fontchar { int code, uni, tex, x, y, w, h, offx, offy, offset, advance; FT_BitmapGlyph color, alpha; };
+
+const char *texdir = "";
+
+const char *texfilename(const char *name, int texnum)
+{
+ static char file[256];
+ snprintf(file, sizeof(file), "%s%d.png", name, texnum);
+ return file;
+}
+
+const char *texname(const char *name, int texnum)
+{
+ static char file[512];
+ snprintf(file, sizeof(file), "<grey>%s%s", texdir, texfilename(name, texnum));
+ return file;
+}
+
+void writetexs(const char *name, struct fontchar *chars, int numchars, int numtexs, int tw, int th)
+{
+ int tex;
+ uchar *pixels = (uchar *)malloc(tw*th*2);
+ if(!pixels) fatal("cube2font: failed allocating textures");
+ for(tex = 0; tex < numtexs; tex++)
+ {
+ const char *file = texfilename(name, tex);
+ int texchars = 0, i;
+ uchar *dst, *src;
+ memset(pixels, 0, tw*th*2);
+ for(i = 0; i < numchars; i++)
+ {
+ struct fontchar *c = &chars[i];
+ int x, y;
+ if(c->tex != tex) continue;
+ texchars++;
+ dst = &pixels[2*((c->y + c->offy - c->color->top)*tw + c->x + c->color->left - c->offx)];
+ src = (uchar *)c->color->bitmap.buffer;
+ for(y = 0; y < c->color->bitmap.rows; y++)
+ {
+ for(x = 0; x < c->color->bitmap.width; x++)
+ dst[2*x] = src[x];
+ src += c->color->bitmap.pitch;
+ dst += 2*tw;
+ }
+ dst = &pixels[2*((c->y + c->offy - c->alpha->top)*tw + c->x + c->alpha->left - c->offx)];
+ src = (uchar *)c->alpha->bitmap.buffer;
+ for(y = 0; y < c->alpha->bitmap.rows; y++)
+ {
+ for(x = 0; x < c->alpha->bitmap.width; x++)
+ dst[2*x+1] = src[x];
+ src += c->alpha->bitmap.pitch;
+ dst += 2*tw;
+ }
+ }
+ printf("cube2font: writing %d chars to %s\n", texchars, file);
+ savepng(file, pixels, tw, th, 2, 0);
+ }
+ free(pixels);
+}
+
+void writecfg(const char *name, struct fontchar *chars, int numchars, int x1, int y1, int x2, int y2, int sw, int sh, int argc, char **argv)
+{
+ FILE *f;
+ char file[256];
+ int i, lastcode = 0, lasttex = 0;
+ snprintf(file, sizeof(file), "%s.cfg", name);
+ f = fopen(file, "w");
+ if(!f) fatal("cube2font: failed writing %s", file);
+ printf("cube2font: writing %d chars to %s\n", numchars, file);
+ fprintf(f, "//");
+ for(i = 1; i < argc; i++)
+ fprintf(f, " %s", argv[i]);
+ fprintf(f, "\n");
+ fprintf(f, "font \"%s\" \"%s\" %d %d\n", name, texname(name, 0), sw, sh);
+ for(i = 0; i < numchars; i++)
+ {
+ struct fontchar *c = &chars[i];
+ if(!lastcode && lastcode < c->code)
+ {
+ fprintf(f, "fontoffset \"%s\"\n", encodeutf8(c->uni));
+ lastcode = c->code;
+ }
+ else if(lastcode < c->code)
+ {
+ if(lastcode + 1 == c->code)
+ fprintf(f, "fontskip // %d\n", lastcode);
+ else
+ fprintf(f, "fontskip %d // %d .. %d\n", c->code - lastcode, lastcode, c->code-1);
+ lastcode = c->code;
+ }
+ if(lasttex != c->tex)
+ {
+ fprintf(f, "\nfonttex \"%s\"\n", texname(name, c->tex));
+ lasttex = c->tex;
+ }
+ if(c->code != c->uni)
+ fprintf(f, "fontchar %d %d %d %d %d %d %d // %s (%d -> 0x%X)\n", c->x, c->y, c->w, c->h, c->offx+c->offset, y2-c->offy, c->advance, encodeutf8(c->uni), c->code, c->uni);
+ else
+ fprintf(f, "fontchar %d %d %d %d %d %d %d // %s (%d)\n", c->x, c->y, c->w, c->h, c->offx+c->offset, y2-c->offy, c->advance, encodeutf8(c->uni), c->code);
+ lastcode++;
+ }
+ fclose(f);
+}
+
+int groupchar(int c)
+{
+ switch(c)
+ {
+ case 0x152: case 0x153: case 0x178: return 1;
+ }
+ if(c < 127 || c >= 0x2000) return 0;
+ if(c < 0x100) return 1;
+ if(c < 0x400) return 2;
+ return 3;
+}
+
+int sortchars(const void *x, const void *y)
+{
+ const struct fontchar *xc = *(const struct fontchar **)x, *yc = *(const struct fontchar **)y;
+ int xg = groupchar(xc->uni), yg = groupchar(yc->uni);
+ if(xg < yg) return -1;
+ if(xg > yg) return 1;
+ if(xc->h != yc->h) return yc->h - xc->h;
+ if(xc->w != yc->w) return yc->w - xc->w;
+ return yc->uni - xc->uni;
+}
+
+int scorechar(struct fontchar *f, int pad, int tw, int th, int rw, int rh, int ry)
+{
+ int score = 0;
+ if(rw + f->w > tw) { ry += rh + pad; score = 1; }
+ if(ry + f->h > th) score = 2;
+ return score;
+}
+
+int main(int argc, char **argv)
+{
+ FT_Library l;
+ FT_Face f;
+ FT_Stroker s, s2;
+ int i, pad, offset, advance, w, h, tw, th, c, trial = -2, rw = 0, rh = 0, ry = 0, x1 = INT_MAX, x2 = INT_MIN, y1 = INT_MAX, y2 = INT_MIN, w2 = 0, h2 = 0, sw = 0, sh = 0;
+ float outborder = 0, inborder = 0;
+ struct fontchar chars[256];
+ struct fontchar *order[256];
+ int numchars = 0, numtex = 0;
+ if(argc < 11)
+ fatal("Usage: cube2font infile outfile outborder[:inborder] pad offset advance charwidth charheight texwidth texheight [spacewidth spaceheight texdir]");
+ sscanf(argv[3], "%f:%f", &outborder, &inborder);
+ pad = atoi(argv[4]);
+ offset = atoi(argv[5]);
+ advance = atoi(argv[6]);
+ w = atoi(argv[7]);
+ h = atoi(argv[8]);
+ tw = atoi(argv[9]);
+ th = atoi(argv[10]);
+ if(argc > 11) sw = atoi(argv[11]);
+ if(argc > 12) sh = atoi(argv[12]);
+ if(argc > 13) texdir = argv[13];
+ if(FT_Init_FreeType(&l))
+ fatal("cube2font: failed initing freetype");
+ if(FT_New_Face(l, argv[1], 0, &f) ||
+ FT_Set_Charmap(f, f->charmaps[0]) ||
+ FT_Set_Pixel_Sizes(f, w, h) ||
+ FT_Stroker_New(l, &s) ||
+ FT_Stroker_New(l, &s2))
+ fatal("cube2font: failed loading font %s", argv[1]);
+ if(outborder > 0) FT_Stroker_Set(s, (FT_Fixed)(outborder * 64), FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0);
+ if(inborder > 0) FT_Stroker_Set(s2, (FT_Fixed)(inborder * 64), FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0);
+ for(c = 0; c < 256; c++) if(iscubeprint(c))
+ {
+ FT_Glyph p, p2;
+ FT_BitmapGlyph b, b2;
+ struct fontchar *dst = &chars[numchars];
+ dst->code = c;
+ dst->uni = cube2uni(c);
+ if(FT_Load_Char(f, dst->uni, FT_LOAD_DEFAULT))
+ fatal("cube2font: failed loading character %s", encodeutf8(dst->uni));
+ FT_Get_Glyph(f->glyph, &p);
+ p2 = p;
+ if(outborder > 0) FT_Glyph_StrokeBorder(&p, s, 0, 0);
+ if(inborder > 0) FT_Glyph_StrokeBorder(&p2, s2, 1, 0);
+ FT_Glyph_To_Bitmap(&p, FT_RENDER_MODE_NORMAL, 0, 1);
+ if(inborder > 0 || outborder > 0) FT_Glyph_To_Bitmap(&p2, FT_RENDER_MODE_NORMAL, 0, 1);
+ else p2 = p;
+ b = (FT_BitmapGlyph)p;
+ b2 = (FT_BitmapGlyph)p2;
+ dst->tex = -1;
+ dst->x = INT_MIN;
+ dst->y = INT_MIN;
+ dst->offx = imin(b->left, b2->left);
+ dst->offy = imax(b->top, b2->top);
+ dst->offset = offset;
+ dst->advance = offset + ((p->advance.x+0xFFFF)>>16) + advance;
+ dst->w = imax(b->left + b->bitmap.width, b2->left + b2->bitmap.width) - dst->offx;
+ dst->h = dst->offy - imin(b->top - b->bitmap.rows, b2->top - b2->bitmap.rows);
+ dst->alpha = b;
+ dst->color = b2;
+ order[numchars++] = dst;
+ }
+ qsort(order, numchars, sizeof(order[0]), sortchars);
+ for(i = 0; i < numchars;)
+ {
+ struct fontchar *dst;
+ int j, k, trial0, prevscore, dstscore, fitscore;
+ for(trial0 = trial, prevscore = -1; (trial -= 2) >= trial0-512;)
+ {
+ int g, fw = rw, fh = rh, fy = ry, curscore = 0, reused = 0;
+ for(j = i; j < numchars; j++)
+ {
+ dst = order[j];
+ if(dst->tex >= 0 || dst->tex <= trial) continue;
+ g = groupchar(dst->uni);
+ dstscore = scorechar(dst, pad, tw, th, fw, fh, fy);
+ for(k = j; k < numchars; k++)
+ {
+ struct fontchar *fit = order[k];
+ if(fit->tex >= 0 || fit->tex <= trial) continue;
+ if(fit->tex >= trial0 && groupchar(fit->uni) != g) break;
+ fitscore = scorechar(fit, pad, tw, th, fw, fh, fy);
+ if(fitscore < dstscore || (fitscore == dstscore && fit->h > dst->h))
+ {
+ dst = fit;
+ dstscore = fitscore;
+ }
+ }
+ if(fw + dst->w > tw)
+ {
+ fy += fh + pad;
+ fw = fh = 0;
+ }
+ if(fy + dst->h > th)
+ {
+ fy = fw = fh = 0;
+ if(curscore > 0) break;
+ }
+ if(dst->tex >= trial+1 && dst->tex <= trial+2) { dst->tex = trial; reused++; }
+ else dst->tex = trial;
+ fw += dst->w + pad;
+ fh = imax(fh, dst->h);
+ if(dst != order[j]) --j;
+ curscore++;
+ }
+ if(reused < prevscore || curscore <= prevscore) break;
+ prevscore = curscore;
+ }
+ for(; i < numchars; i++)
+ {
+ dst = order[i];
+ if(dst->tex >= 0) continue;
+ dstscore = scorechar(dst, pad, tw, th, rw, rh, ry);
+ for(j = i; j < numchars; j++)
+ {
+ struct fontchar *fit = order[j];
+ if(fit->tex < trial || fit->tex > trial+2) continue;
+ fitscore = scorechar(fit, pad, tw, th, rw, rh, ry);
+ if(fitscore < dstscore || (fitscore == dstscore && fit->h > dst->h))
+ {
+ dst = fit;
+ dstscore = fitscore;
+ }
+ }
+ if(dst->tex < trial || dst->tex > trial+2) break;
+ if(rw + dst->w > tw)
+ {
+ ry += rh + pad;
+ rw = rh = 0;
+ }
+ if(ry + dst->h > th)
+ {
+ ry = rw = rh = 0;
+ numtex++;
+ }
+ dst->tex = numtex;
+ dst->x = rw;
+ dst->y = ry;
+ rw += dst->w + pad;
+ rh = imax(rh, dst->h);
+ y1 = imin(y1, dst->offy - dst->h);
+ y2 = imax(y2, dst->offy);
+ x1 = imin(x1, dst->offx);
+ x2 = imax(x2, dst->offx + dst->w);
+ w2 = imax(w2, dst->w);
+ h2 = imax(h2, dst->h);
+ if(dst != order[i]) --i;
+ }
+ }
+ if(rh > 0) numtex++;
+#if 0
+ if(sw <= 0)
+ {
+ if(FT_Load_Char(f, ' ', FT_LOAD_DEFAULT))
+ fatal("cube2font: failed loading space character");
+ sw = (f->glyph->advance.x+0x3F)>>6;
+ }
+#endif
+ if(sh <= 0) sh = y2 - y1;
+ if(sw <= 0) sw = sh/3;
+ writetexs(argv[2], chars, numchars, numtex, tw, th);
+ writecfg(argv[2], chars, numchars, x1, y1, x2, y2, sw, sh, argc, argv);
+ for(i = 0; i < numchars; i++)
+ {
+ if(chars[i].alpha != chars[i].color) FT_Done_Glyph((FT_Glyph)chars[i].alpha);
+ FT_Done_Glyph((FT_Glyph)chars[i].color);
+ }
+ FT_Stroker_Done(s);
+ FT_Stroker_Done(s2);
+ FT_Done_FreeType(l);
+ printf("cube2font: (%d, %d) .. (%d, %d) = (%d, %d) / (%d, %d), %d texs\n", x1, y1, x2, y2, x2 - x1, y2 - y1, w2, h2, numtex);
+ return EXIT_SUCCESS;
+}
+
diff --git a/src/shared/ents.h b/src/shared/ents.h new file mode 100644 index 0000000..f4da8f5 --- /dev/null +++ b/src/shared/ents.h @@ -0,0 +1,237 @@ +// this file defines static map entities ("entity") and dynamic entities (players/monsters, "dynent") +// the gamecode extends these types to add game specific functionality + +// ET_*: the only static entity types dictated by the engine... rest are gamecode dependent + +enum { ET_EMPTY=0, ET_LIGHT, ET_MAPMODEL, ET_PLAYERSTART, ET_ENVMAP, ET_PARTICLES, ET_SOUND, ET_SPOTLIGHT, ET_GAMESPECIFIC }; + +struct entity // persistent map entity +{ + vec o; // position + short attr1, attr2, attr3, attr4, attr5; + uchar type; // type is one of the above + uchar reserved; +}; + +struct entitylight +{ + vec color, dir; + int millis; + + entitylight() : color(1, 1, 1), dir(0, 0, 1), millis(-1) {} +}; + +enum +{ + EF_NOVIS = 1<<0, + EF_NOSHADOW = 1<<1, + EF_NOCOLLIDE = 1<<2, + EF_ANIM = 1<<3, + EF_OCTA = 1<<4, + EF_RENDER = 1<<5, + EF_SOUND = 1<<6, + EF_SPAWNED = 1<<7, + EF_NOPICKUP = 1<<8 +}; + +struct extentity : entity // part of the entity that doesn't get saved to disk +{ + int flags; // the only dynamic state of a map entity + entitylight light; + extentity *attached; + + extentity() : flags(0), attached(NULL) {} + + bool spawned() const { return (flags&EF_SPAWNED) != 0; } + void setspawned(bool val) { if(val) flags |= EF_SPAWNED; else flags &= ~EF_SPAWNED; } + void setspawned() { flags |= EF_SPAWNED; } + void clearspawned() { flags &= ~EF_SPAWNED; } + + bool nopickup() const { return (flags&EF_NOPICKUP) != 0; } + void setnopickup(bool val) { if(val) flags |= EF_NOPICKUP; else flags &= ~EF_NOPICKUP; } + void setnopickup() { flags |= EF_NOPICKUP; } + void clearnopickup() { flags &= ~EF_NOPICKUP; } +}; + +#define MAXENTS 10000 + +//extern vector<extentity *> ents; // map entities + +enum { CS_ALIVE = 0, CS_DEAD, CS_SPAWNING, CS_LAGGED, CS_EDITING, CS_SPECTATOR }; + +enum { PHYS_FLOAT = 0, PHYS_FALL, PHYS_SLIDE, PHYS_SLOPE, PHYS_FLOOR, PHYS_STEP_UP, PHYS_STEP_DOWN, PHYS_BOUNCE }; + +enum { ENT_PLAYER = 0, ENT_AI, ENT_INANIMATE, ENT_CAMERA, ENT_BOUNCE }; + +enum { COLLIDE_NONE = 0, COLLIDE_ELLIPSE, COLLIDE_OBB, COLLIDE_ELLIPSE_PRECISE }; + +struct physent // base entity type, can be affected by physics +{ + vec o, vel, falling; // origin, velocity + vec deltapos, newpos; // movement interpolation + float yaw, pitch, roll; + float maxspeed; // cubes per second, 100 for player + float radius, eyeheight, aboveeye; // bounding box size + float xradius, yradius, zmargin; + vec floor; // the normal of floor the dynent is on + + ushort timeinair; + uchar inwater; + bool jumping; + schar move, strafe; + + uchar physstate; // one of PHYS_* above + uchar state, editstate; // one of CS_* above + uchar type; // one of ENT_* above + uchar collidetype; // one of COLLIDE_* above + + bool blocked; // used by physics to signal ai + + physent() : o(0, 0, 0), deltapos(0, 0, 0), newpos(0, 0, 0), yaw(0), pitch(0), roll(0), maxspeed(100), + radius(4.1f), eyeheight(14), aboveeye(1), xradius(4.1f), yradius(4.1f), zmargin(0), + state(CS_ALIVE), editstate(CS_ALIVE), type(ENT_PLAYER), + collidetype(COLLIDE_ELLIPSE), + blocked(false) + { reset(); } + + void resetinterp() + { + newpos = o; + deltapos = vec(0, 0, 0); + } + + void reset() + { + inwater = 0; + timeinair = 0; + jumping = false; + strafe = move = 0; + physstate = PHYS_FALL; + vel = falling = vec(0, 0, 0); + floor = vec(0, 0, 1); + } + + vec feetpos(float offset = 0) const { return vec(o).add(vec(0, 0, offset - eyeheight)); } + vec headpos(float offset = 0) const { return vec(o).add(vec(0, 0, offset)); } + + bool maymove() const { return timeinair || physstate < PHYS_FLOOR || vel.squaredlen() > 1e-4f || deltapos.squaredlen() > 1e-4f; } +}; + +enum +{ + ANIM_DEAD = 0, ANIM_DYING, ANIM_IDLE, + ANIM_FORWARD, ANIM_BACKWARD, ANIM_LEFT, ANIM_RIGHT, + ANIM_HOLD1, ANIM_HOLD2, ANIM_HOLD3, ANIM_HOLD4, ANIM_HOLD5, ANIM_HOLD6, ANIM_HOLD7, + ANIM_ATTACK1, ANIM_ATTACK2, ANIM_ATTACK3, ANIM_ATTACK4, ANIM_ATTACK5, ANIM_ATTACK6, ANIM_ATTACK7, + ANIM_PAIN, + ANIM_JUMP, ANIM_SINK, ANIM_SWIM, + ANIM_EDIT, ANIM_LAG, ANIM_TAUNT, ANIM_WIN, ANIM_LOSE, + ANIM_GUN_IDLE, ANIM_GUN_SHOOT, + ANIM_VWEP_IDLE, ANIM_VWEP_SHOOT, ANIM_SHIELD, ANIM_POWERUP, + ANIM_MAPMODEL, ANIM_TRIGGER, + NUMANIMS +}; + +static const char * const animnames[] = +{ + "dead", "dying", "idle", + "forward", "backward", "left", "right", + "hold 1", "hold 2", "hold 3", "hold 4", "hold 5", "hold 6", "hold 7", + "attack 1", "attack 2", "attack 3", "attack 4", "attack 5", "attack 6", "attack 7", + "pain", + "jump", "sink", "swim", + "edit", "lag", "taunt", "win", "lose", + "gun idle", "gun shoot", + "vwep idle", "vwep shoot", "shield", "powerup", + "mapmodel", "trigger" +}; + +#define ANIM_ALL 0x7F +#define ANIM_INDEX 0x7F +#define ANIM_LOOP (1<<7) +#define ANIM_START (1<<8) +#define ANIM_END (1<<9) +#define ANIM_REVERSE (1<<10) +#define ANIM_CLAMP (ANIM_START|ANIM_END) +#define ANIM_DIR 0x780 +#define ANIM_SECONDARY 11 +#define ANIM_NOSKIN (1<<22) +#define ANIM_SETTIME (1<<23) +#define ANIM_FULLBRIGHT (1<<24) +#define ANIM_REUSE (1<<25) +#define ANIM_NORENDER (1<<26) +#define ANIM_RAGDOLL (1<<27) +#define ANIM_SETSPEED (1<<28) +#define ANIM_NOPITCH (1<<29) +#define ANIM_GHOST (1<<30) +#define ANIM_FLAGS (0x1FF<<22) + +struct animinfo // description of a character's animation +{ + int anim, frame, range, basetime; + float speed; + uint varseed; + + animinfo() : anim(0), frame(0), range(0), basetime(0), speed(100.0f), varseed(0) { } + + bool operator==(const animinfo &o) const { return frame==o.frame && range==o.range && (anim&(ANIM_SETTIME|ANIM_DIR))==(o.anim&(ANIM_SETTIME|ANIM_DIR)) && (anim&ANIM_SETTIME || basetime==o.basetime) && speed==o.speed; } + bool operator!=(const animinfo &o) const { return frame!=o.frame || range!=o.range || (anim&(ANIM_SETTIME|ANIM_DIR))!=(o.anim&(ANIM_SETTIME|ANIM_DIR)) || (!(anim&ANIM_SETTIME) && basetime!=o.basetime) || speed!=o.speed; } +}; + +struct animinterpinfo // used for animation blending of animated characters +{ + animinfo prev, cur; + int lastswitch; + void *lastmodel; + + animinterpinfo() : lastswitch(-1), lastmodel(NULL) {} + + void reset() { lastswitch = -1; } +}; + +#define MAXANIMPARTS 3 + +struct occludequery; +struct ragdolldata; + +struct dynent : physent // animated characters, or characters that can receive input +{ + bool k_left, k_right, k_up, k_down; // see input code + + entitylight light; + animinterpinfo animinterp[MAXANIMPARTS]; + ragdolldata *ragdoll; + occludequery *query; + int lastrendered; + uchar occluded; + + dynent() : ragdoll(NULL), query(NULL), lastrendered(0), occluded(0) + { + reset(); + } + + ~dynent() + { +#ifndef STANDALONE + extern void cleanragdoll(dynent *d); + if(ragdoll) cleanragdoll(this); +#endif + } + + void stopmoving() + { + k_left = k_right = k_up = k_down = jumping = false; + move = strafe = 0; + } + + void reset() + { + physent::reset(); + stopmoving(); + loopi(MAXANIMPARTS) animinterp[i].reset(); + } + + vec abovehead() { return vec(o).add(vec(0, 0, aboveeye+4)); } +}; + + diff --git a/src/shared/geom.cpp b/src/shared/geom.cpp new file mode 100644 index 0000000..43206e1 --- /dev/null +++ b/src/shared/geom.cpp @@ -0,0 +1,257 @@ + +#include "cube.h" + +static inline double det2x2(double a, double b, double c, double d) { return a*d - b*c; } +static inline double det3x3(double a1, double a2, double a3, + double b1, double b2, double b3, + double c1, double c2, double c3) +{ + return a1 * det2x2(b2, b3, c2, c3) + - b1 * det2x2(a2, a3, c2, c3) + + c1 * det2x2(a2, a3, b2, b3); +} + +bool matrix4::invert(const matrix4 &m, double mindet) +{ + double a1 = m.a.x, a2 = m.a.y, a3 = m.a.z, a4 = m.a.w, + b1 = m.b.x, b2 = m.b.y, b3 = m.b.z, b4 = m.b.w, + c1 = m.c.x, c2 = m.c.y, c3 = m.c.z, c4 = m.c.w, + d1 = m.d.x, d2 = m.d.y, d3 = m.d.z, d4 = m.d.w, + det1 = det3x3(b2, b3, b4, c2, c3, c4, d2, d3, d4), + det2 = -det3x3(a2, a3, a4, c2, c3, c4, d2, d3, d4), + det3 = det3x3(a2, a3, a4, b2, b3, b4, d2, d3, d4), + det4 = -det3x3(a2, a3, a4, b2, b3, b4, c2, c3, c4), + det = a1*det1 + b1*det2 + c1*det3 + d1*det4; + + if(fabs(det) < mindet) return false; + + double invdet = 1/det; + + a.x = det1 * invdet; + a.y = det2 * invdet; + a.z = det3 * invdet; + a.w = det4 * invdet; + + b.x = -det3x3(b1, b3, b4, c1, c3, c4, d1, d3, d4) * invdet; + b.y = det3x3(a1, a3, a4, c1, c3, c4, d1, d3, d4) * invdet; + b.z = -det3x3(a1, a3, a4, b1, b3, b4, d1, d3, d4) * invdet; + b.w = det3x3(a1, a3, a4, b1, b3, b4, c1, c3, c4) * invdet; + + c.x = det3x3(b1, b2, b4, c1, c2, c4, d1, d2, d4) * invdet; + c.y = -det3x3(a1, a2, a4, c1, c2, c4, d1, d2, d4) * invdet; + c.z = det3x3(a1, a2, a4, b1, b2, b4, d1, d2, d4) * invdet; + c.w = -det3x3(a1, a2, a4, b1, b2, b4, c1, c2, c4) * invdet; + + d.x = -det3x3(b1, b2, b3, c1, c2, c3, d1, d2, d3) * invdet; + d.y = det3x3(a1, a2, a3, c1, c2, c3, d1, d2, d3) * invdet; + d.z = -det3x3(a1, a2, a3, b1, b2, b3, d1, d2, d3) * invdet; + d.w = det3x3(a1, a2, a3, b1, b2, b3, c1, c2, c3) * invdet; + + return true; +} + +bool raysphereintersect(const vec ¢er, float radius, const vec &o, const vec &ray, float &dist) +{ + vec c(center); + c.sub(o); + float v = c.dot(ray), + inside = radius*radius - c.squaredlen(); + if(inside<0 && v<0) return false; + float d = inside + v*v; + if(d<0) return false; + dist = v - sqrt(d); + return true; +} + +bool rayboxintersect(const vec &b, const vec &s, const vec &o, const vec &ray, float &dist, int &orient) +{ + loop(d, 3) if(ray[d]) + { + int dc = ray[d]<0 ? 1 : 0; + float pdist = (b[d]+s[d]*dc - o[d]) / ray[d]; + vec v(ray); + v.mul(pdist).add(o); + if(v[R[d]] >= b[R[d]] && v[R[d]] <= b[R[d]]+s[R[d]] + && v[C[d]] >= b[C[d]] && v[C[d]] <= b[C[d]]+s[C[d]]) + { + dist = pdist; + orient = 2*d+dc; + return true; + } + } + return false; +} + +bool linecylinderintersect(const vec &from, const vec &to, const vec &start, const vec &end, float radius, float &dist) +{ + vec d(end), m(from), n(to); + d.sub(start); + m.sub(start); + n.sub(from); + float md = m.dot(d), + nd = n.dot(d), + dd = d.squaredlen(); + if(md < 0 && md + nd < 0) return false; + if(md > dd && md + nd > dd) return false; + float nn = n.squaredlen(), + mn = m.dot(n), + a = dd*nn - nd*nd, + k = m.squaredlen() - radius*radius, + c = dd*k - md*md; + if(fabs(a) < 0.005f) + { + if(c > 0) return false; + if(md < 0) dist = -mn / nn; + else if(md > dd) dist = (nd - mn) / nn; + else dist = 0; + return true; + } + else if(c > 0) + { + float b = dd*mn - nd*md, + discrim = b*b - a*c; + if(discrim < 0) return false; + dist = (-b - sqrtf(discrim)) / a; + } + else dist = 0; + float offset = md + dist*nd; + if(offset < 0) + { + if(nd <= 0) return false; + dist = -md / nd; + if(k + dist*(2*mn + dist*nn) > 0) return false; + } + else if(offset > dd) + { + if(nd >= 0) return false; + dist = (dd - md) / nd; + if(k + dd - 2*md + dist*(2*(mn-nd) + dist*nn) > 0) return false; + } + return dist >= 0 && dist <= 1; +} + +extern const vec2 sincos360[721] = +{ + vec2(1.00000000, 0.00000000), vec2(0.99984770, 0.01745241), vec2(0.99939083, 0.03489950), vec2(0.99862953, 0.05233596), vec2(0.99756405, 0.06975647), vec2(0.99619470, 0.08715574), // 0 + vec2(0.99452190, 0.10452846), vec2(0.99254615, 0.12186934), vec2(0.99026807, 0.13917310), vec2(0.98768834, 0.15643447), vec2(0.98480775, 0.17364818), vec2(0.98162718, 0.19080900), // 6 + vec2(0.97814760, 0.20791169), vec2(0.97437006, 0.22495105), vec2(0.97029573, 0.24192190), vec2(0.96592583, 0.25881905), vec2(0.96126170, 0.27563736), vec2(0.95630476, 0.29237170), // 12 + vec2(0.95105652, 0.30901699), vec2(0.94551858, 0.32556815), vec2(0.93969262, 0.34202014), vec2(0.93358043, 0.35836795), vec2(0.92718385, 0.37460659), vec2(0.92050485, 0.39073113), // 18 + vec2(0.91354546, 0.40673664), vec2(0.90630779, 0.42261826), vec2(0.89879405, 0.43837115), vec2(0.89100652, 0.45399050), vec2(0.88294759, 0.46947156), vec2(0.87461971, 0.48480962), // 24 + vec2(0.86602540, 0.50000000), vec2(0.85716730, 0.51503807), vec2(0.84804810, 0.52991926), vec2(0.83867057, 0.54463904), vec2(0.82903757, 0.55919290), vec2(0.81915204, 0.57357644), // 30 + vec2(0.80901699, 0.58778525), vec2(0.79863551, 0.60181502), vec2(0.78801075, 0.61566148), vec2(0.77714596, 0.62932039), vec2(0.76604444, 0.64278761), vec2(0.75470958, 0.65605903), // 36 + vec2(0.74314483, 0.66913061), vec2(0.73135370, 0.68199836), vec2(0.71933980, 0.69465837), vec2(0.70710678, 0.70710678), vec2(0.69465837, 0.71933980), vec2(0.68199836, 0.73135370), // 42 + vec2(0.66913061, 0.74314483), vec2(0.65605903, 0.75470958), vec2(0.64278761, 0.76604444), vec2(0.62932039, 0.77714596), vec2(0.61566148, 0.78801075), vec2(0.60181502, 0.79863551), // 48 + vec2(0.58778525, 0.80901699), vec2(0.57357644, 0.81915204), vec2(0.55919290, 0.82903757), vec2(0.54463904, 0.83867057), vec2(0.52991926, 0.84804810), vec2(0.51503807, 0.85716730), // 54 + vec2(0.50000000, 0.86602540), vec2(0.48480962, 0.87461971), vec2(0.46947156, 0.88294759), vec2(0.45399050, 0.89100652), vec2(0.43837115, 0.89879405), vec2(0.42261826, 0.90630779), // 60 + vec2(0.40673664, 0.91354546), vec2(0.39073113, 0.92050485), vec2(0.37460659, 0.92718385), vec2(0.35836795, 0.93358043), vec2(0.34202014, 0.93969262), vec2(0.32556815, 0.94551858), // 66 + vec2(0.30901699, 0.95105652), vec2(0.29237170, 0.95630476), vec2(0.27563736, 0.96126170), vec2(0.25881905, 0.96592583), vec2(0.24192190, 0.97029573), vec2(0.22495105, 0.97437006), // 72 + vec2(0.20791169, 0.97814760), vec2(0.19080900, 0.98162718), vec2(0.17364818, 0.98480775), vec2(0.15643447, 0.98768834), vec2(0.13917310, 0.99026807), vec2(0.12186934, 0.99254615), // 78 + vec2(0.10452846, 0.99452190), vec2(0.08715574, 0.99619470), vec2(0.06975647, 0.99756405), vec2(0.05233596, 0.99862953), vec2(0.03489950, 0.99939083), vec2(0.01745241, 0.99984770), // 84 + vec2(0.00000000, 1.00000000), vec2(-0.01745241, 0.99984770), vec2(-0.03489950, 0.99939083), vec2(-0.05233596, 0.99862953), vec2(-0.06975647, 0.99756405), vec2(-0.08715574, 0.99619470), // 90 + vec2(-0.10452846, 0.99452190), vec2(-0.12186934, 0.99254615), vec2(-0.13917310, 0.99026807), vec2(-0.15643447, 0.98768834), vec2(-0.17364818, 0.98480775), vec2(-0.19080900, 0.98162718), // 96 + vec2(-0.20791169, 0.97814760), vec2(-0.22495105, 0.97437006), vec2(-0.24192190, 0.97029573), vec2(-0.25881905, 0.96592583), vec2(-0.27563736, 0.96126170), vec2(-0.29237170, 0.95630476), // 102 + vec2(-0.30901699, 0.95105652), vec2(-0.32556815, 0.94551858), vec2(-0.34202014, 0.93969262), vec2(-0.35836795, 0.93358043), vec2(-0.37460659, 0.92718385), vec2(-0.39073113, 0.92050485), // 108 + vec2(-0.40673664, 0.91354546), vec2(-0.42261826, 0.90630779), vec2(-0.43837115, 0.89879405), vec2(-0.45399050, 0.89100652), vec2(-0.46947156, 0.88294759), vec2(-0.48480962, 0.87461971), // 114 + vec2(-0.50000000, 0.86602540), vec2(-0.51503807, 0.85716730), vec2(-0.52991926, 0.84804810), vec2(-0.54463904, 0.83867057), vec2(-0.55919290, 0.82903757), vec2(-0.57357644, 0.81915204), // 120 + vec2(-0.58778525, 0.80901699), vec2(-0.60181502, 0.79863551), vec2(-0.61566148, 0.78801075), vec2(-0.62932039, 0.77714596), vec2(-0.64278761, 0.76604444), vec2(-0.65605903, 0.75470958), // 126 + vec2(-0.66913061, 0.74314483), vec2(-0.68199836, 0.73135370), vec2(-0.69465837, 0.71933980), vec2(-0.70710678, 0.70710678), vec2(-0.71933980, 0.69465837), vec2(-0.73135370, 0.68199836), // 132 + vec2(-0.74314483, 0.66913061), vec2(-0.75470958, 0.65605903), vec2(-0.76604444, 0.64278761), vec2(-0.77714596, 0.62932039), vec2(-0.78801075, 0.61566148), vec2(-0.79863551, 0.60181502), // 138 + vec2(-0.80901699, 0.58778525), vec2(-0.81915204, 0.57357644), vec2(-0.82903757, 0.55919290), vec2(-0.83867057, 0.54463904), vec2(-0.84804810, 0.52991926), vec2(-0.85716730, 0.51503807), // 144 + vec2(-0.86602540, 0.50000000), vec2(-0.87461971, 0.48480962), vec2(-0.88294759, 0.46947156), vec2(-0.89100652, 0.45399050), vec2(-0.89879405, 0.43837115), vec2(-0.90630779, 0.42261826), // 150 + vec2(-0.91354546, 0.40673664), vec2(-0.92050485, 0.39073113), vec2(-0.92718385, 0.37460659), vec2(-0.93358043, 0.35836795), vec2(-0.93969262, 0.34202014), vec2(-0.94551858, 0.32556815), // 156 + vec2(-0.95105652, 0.30901699), vec2(-0.95630476, 0.29237170), vec2(-0.96126170, 0.27563736), vec2(-0.96592583, 0.25881905), vec2(-0.97029573, 0.24192190), vec2(-0.97437006, 0.22495105), // 162 + vec2(-0.97814760, 0.20791169), vec2(-0.98162718, 0.19080900), vec2(-0.98480775, 0.17364818), vec2(-0.98768834, 0.15643447), vec2(-0.99026807, 0.13917310), vec2(-0.99254615, 0.12186934), // 168 + vec2(-0.99452190, 0.10452846), vec2(-0.99619470, 0.08715574), vec2(-0.99756405, 0.06975647), vec2(-0.99862953, 0.05233596), vec2(-0.99939083, 0.03489950), vec2(-0.99984770, 0.01745241), // 174 + vec2(-1.00000000, 0.00000000), vec2(-0.99984770, -0.01745241), vec2(-0.99939083, -0.03489950), vec2(-0.99862953, -0.05233596), vec2(-0.99756405, -0.06975647), vec2(-0.99619470, -0.08715574), // 180 + vec2(-0.99452190, -0.10452846), vec2(-0.99254615, -0.12186934), vec2(-0.99026807, -0.13917310), vec2(-0.98768834, -0.15643447), vec2(-0.98480775, -0.17364818), vec2(-0.98162718, -0.19080900), // 186 + vec2(-0.97814760, -0.20791169), vec2(-0.97437006, -0.22495105), vec2(-0.97029573, -0.24192190), vec2(-0.96592583, -0.25881905), vec2(-0.96126170, -0.27563736), vec2(-0.95630476, -0.29237170), // 192 + vec2(-0.95105652, -0.30901699), vec2(-0.94551858, -0.32556815), vec2(-0.93969262, -0.34202014), vec2(-0.93358043, -0.35836795), vec2(-0.92718385, -0.37460659), vec2(-0.92050485, -0.39073113), // 198 + vec2(-0.91354546, -0.40673664), vec2(-0.90630779, -0.42261826), vec2(-0.89879405, -0.43837115), vec2(-0.89100652, -0.45399050), vec2(-0.88294759, -0.46947156), vec2(-0.87461971, -0.48480962), // 204 + vec2(-0.86602540, -0.50000000), vec2(-0.85716730, -0.51503807), vec2(-0.84804810, -0.52991926), vec2(-0.83867057, -0.54463904), vec2(-0.82903757, -0.55919290), vec2(-0.81915204, -0.57357644), // 210 + vec2(-0.80901699, -0.58778525), vec2(-0.79863551, -0.60181502), vec2(-0.78801075, -0.61566148), vec2(-0.77714596, -0.62932039), vec2(-0.76604444, -0.64278761), vec2(-0.75470958, -0.65605903), // 216 + vec2(-0.74314483, -0.66913061), vec2(-0.73135370, -0.68199836), vec2(-0.71933980, -0.69465837), vec2(-0.70710678, -0.70710678), vec2(-0.69465837, -0.71933980), vec2(-0.68199836, -0.73135370), // 222 + vec2(-0.66913061, -0.74314483), vec2(-0.65605903, -0.75470958), vec2(-0.64278761, -0.76604444), vec2(-0.62932039, -0.77714596), vec2(-0.61566148, -0.78801075), vec2(-0.60181502, -0.79863551), // 228 + vec2(-0.58778525, -0.80901699), vec2(-0.57357644, -0.81915204), vec2(-0.55919290, -0.82903757), vec2(-0.54463904, -0.83867057), vec2(-0.52991926, -0.84804810), vec2(-0.51503807, -0.85716730), // 234 + vec2(-0.50000000, -0.86602540), vec2(-0.48480962, -0.87461971), vec2(-0.46947156, -0.88294759), vec2(-0.45399050, -0.89100652), vec2(-0.43837115, -0.89879405), vec2(-0.42261826, -0.90630779), // 240 + vec2(-0.40673664, -0.91354546), vec2(-0.39073113, -0.92050485), vec2(-0.37460659, -0.92718385), vec2(-0.35836795, -0.93358043), vec2(-0.34202014, -0.93969262), vec2(-0.32556815, -0.94551858), // 246 + vec2(-0.30901699, -0.95105652), vec2(-0.29237170, -0.95630476), vec2(-0.27563736, -0.96126170), vec2(-0.25881905, -0.96592583), vec2(-0.24192190, -0.97029573), vec2(-0.22495105, -0.97437006), // 252 + vec2(-0.20791169, -0.97814760), vec2(-0.19080900, -0.98162718), vec2(-0.17364818, -0.98480775), vec2(-0.15643447, -0.98768834), vec2(-0.13917310, -0.99026807), vec2(-0.12186934, -0.99254615), // 258 + vec2(-0.10452846, -0.99452190), vec2(-0.08715574, -0.99619470), vec2(-0.06975647, -0.99756405), vec2(-0.05233596, -0.99862953), vec2(-0.03489950, -0.99939083), vec2(-0.01745241, -0.99984770), // 264 + vec2(-0.00000000, -1.00000000), vec2(0.01745241, -0.99984770), vec2(0.03489950, -0.99939083), vec2(0.05233596, -0.99862953), vec2(0.06975647, -0.99756405), vec2(0.08715574, -0.99619470), // 270 + vec2(0.10452846, -0.99452190), vec2(0.12186934, -0.99254615), vec2(0.13917310, -0.99026807), vec2(0.15643447, -0.98768834), vec2(0.17364818, -0.98480775), vec2(0.19080900, -0.98162718), // 276 + vec2(0.20791169, -0.97814760), vec2(0.22495105, -0.97437006), vec2(0.24192190, -0.97029573), vec2(0.25881905, -0.96592583), vec2(0.27563736, -0.96126170), vec2(0.29237170, -0.95630476), // 282 + vec2(0.30901699, -0.95105652), vec2(0.32556815, -0.94551858), vec2(0.34202014, -0.93969262), vec2(0.35836795, -0.93358043), vec2(0.37460659, -0.92718385), vec2(0.39073113, -0.92050485), // 288 + vec2(0.40673664, -0.91354546), vec2(0.42261826, -0.90630779), vec2(0.43837115, -0.89879405), vec2(0.45399050, -0.89100652), vec2(0.46947156, -0.88294759), vec2(0.48480962, -0.87461971), // 294 + vec2(0.50000000, -0.86602540), vec2(0.51503807, -0.85716730), vec2(0.52991926, -0.84804810), vec2(0.54463904, -0.83867057), vec2(0.55919290, -0.82903757), vec2(0.57357644, -0.81915204), // 300 + vec2(0.58778525, -0.80901699), vec2(0.60181502, -0.79863551), vec2(0.61566148, -0.78801075), vec2(0.62932039, -0.77714596), vec2(0.64278761, -0.76604444), vec2(0.65605903, -0.75470958), // 306 + vec2(0.66913061, -0.74314483), vec2(0.68199836, -0.73135370), vec2(0.69465837, -0.71933980), vec2(0.70710678, -0.70710678), vec2(0.71933980, -0.69465837), vec2(0.73135370, -0.68199836), // 312 + vec2(0.74314483, -0.66913061), vec2(0.75470958, -0.65605903), vec2(0.76604444, -0.64278761), vec2(0.77714596, -0.62932039), vec2(0.78801075, -0.61566148), vec2(0.79863551, -0.60181502), // 318 + vec2(0.80901699, -0.58778525), vec2(0.81915204, -0.57357644), vec2(0.82903757, -0.55919290), vec2(0.83867057, -0.54463904), vec2(0.84804810, -0.52991926), vec2(0.85716730, -0.51503807), // 324 + vec2(0.86602540, -0.50000000), vec2(0.87461971, -0.48480962), vec2(0.88294759, -0.46947156), vec2(0.89100652, -0.45399050), vec2(0.89879405, -0.43837115), vec2(0.90630779, -0.42261826), // 330 + vec2(0.91354546, -0.40673664), vec2(0.92050485, -0.39073113), vec2(0.92718385, -0.37460659), vec2(0.93358043, -0.35836795), vec2(0.93969262, -0.34202014), vec2(0.94551858, -0.32556815), // 336 + vec2(0.95105652, -0.30901699), vec2(0.95630476, -0.29237170), vec2(0.96126170, -0.27563736), vec2(0.96592583, -0.25881905), vec2(0.97029573, -0.24192190), vec2(0.97437006, -0.22495105), // 342 + vec2(0.97814760, -0.20791169), vec2(0.98162718, -0.19080900), vec2(0.98480775, -0.17364818), vec2(0.98768834, -0.15643447), vec2(0.99026807, -0.13917310), vec2(0.99254615, -0.12186934), // 348 + vec2(0.99452190, -0.10452846), vec2(0.99619470, -0.08715574), vec2(0.99756405, -0.06975647), vec2(0.99862953, -0.05233596), vec2(0.99939083, -0.03489950), vec2(0.99984770, -0.01745241), // 354 + vec2(1.00000000, 0.00000000), vec2(0.99984770, 0.01745241), vec2(0.99939083, 0.03489950), vec2(0.99862953, 0.05233596), vec2(0.99756405, 0.06975647), vec2(0.99619470, 0.08715574), // 360 + vec2(0.99452190, 0.10452846), vec2(0.99254615, 0.12186934), vec2(0.99026807, 0.13917310), vec2(0.98768834, 0.15643447), vec2(0.98480775, 0.17364818), vec2(0.98162718, 0.19080900), // 366 + vec2(0.97814760, 0.20791169), vec2(0.97437006, 0.22495105), vec2(0.97029573, 0.24192190), vec2(0.96592583, 0.25881905), vec2(0.96126170, 0.27563736), vec2(0.95630476, 0.29237170), // 372 + vec2(0.95105652, 0.30901699), vec2(0.94551858, 0.32556815), vec2(0.93969262, 0.34202014), vec2(0.93358043, 0.35836795), vec2(0.92718385, 0.37460659), vec2(0.92050485, 0.39073113), // 378 + vec2(0.91354546, 0.40673664), vec2(0.90630779, 0.42261826), vec2(0.89879405, 0.43837115), vec2(0.89100652, 0.45399050), vec2(0.88294759, 0.46947156), vec2(0.87461971, 0.48480962), // 384 + vec2(0.86602540, 0.50000000), vec2(0.85716730, 0.51503807), vec2(0.84804810, 0.52991926), vec2(0.83867057, 0.54463904), vec2(0.82903757, 0.55919290), vec2(0.81915204, 0.57357644), // 390 + vec2(0.80901699, 0.58778525), vec2(0.79863551, 0.60181502), vec2(0.78801075, 0.61566148), vec2(0.77714596, 0.62932039), vec2(0.76604444, 0.64278761), vec2(0.75470958, 0.65605903), // 396 + vec2(0.74314483, 0.66913061), vec2(0.73135370, 0.68199836), vec2(0.71933980, 0.69465837), vec2(0.70710678, 0.70710678), vec2(0.69465837, 0.71933980), vec2(0.68199836, 0.73135370), // 402 + vec2(0.66913061, 0.74314483), vec2(0.65605903, 0.75470958), vec2(0.64278761, 0.76604444), vec2(0.62932039, 0.77714596), vec2(0.61566148, 0.78801075), vec2(0.60181502, 0.79863551), // 408 + vec2(0.58778525, 0.80901699), vec2(0.57357644, 0.81915204), vec2(0.55919290, 0.82903757), vec2(0.54463904, 0.83867057), vec2(0.52991926, 0.84804810), vec2(0.51503807, 0.85716730), // 414 + vec2(0.50000000, 0.86602540), vec2(0.48480962, 0.87461971), vec2(0.46947156, 0.88294759), vec2(0.45399050, 0.89100652), vec2(0.43837115, 0.89879405), vec2(0.42261826, 0.90630779), // 420 + vec2(0.40673664, 0.91354546), vec2(0.39073113, 0.92050485), vec2(0.37460659, 0.92718385), vec2(0.35836795, 0.93358043), vec2(0.34202014, 0.93969262), vec2(0.32556815, 0.94551858), // 426 + vec2(0.30901699, 0.95105652), vec2(0.29237170, 0.95630476), vec2(0.27563736, 0.96126170), vec2(0.25881905, 0.96592583), vec2(0.24192190, 0.97029573), vec2(0.22495105, 0.97437006), // 432 + vec2(0.20791169, 0.97814760), vec2(0.19080900, 0.98162718), vec2(0.17364818, 0.98480775), vec2(0.15643447, 0.98768834), vec2(0.13917310, 0.99026807), vec2(0.12186934, 0.99254615), // 438 + vec2(0.10452846, 0.99452190), vec2(0.08715574, 0.99619470), vec2(0.06975647, 0.99756405), vec2(0.05233596, 0.99862953), vec2(0.03489950, 0.99939083), vec2(0.01745241, 0.99984770), // 444 + vec2(0.00000000, 1.00000000), vec2(-0.01745241, 0.99984770), vec2(-0.03489950, 0.99939083), vec2(-0.05233596, 0.99862953), vec2(-0.06975647, 0.99756405), vec2(-0.08715574, 0.99619470), // 450 + vec2(-0.10452846, 0.99452190), vec2(-0.12186934, 0.99254615), vec2(-0.13917310, 0.99026807), vec2(-0.15643447, 0.98768834), vec2(-0.17364818, 0.98480775), vec2(-0.19080900, 0.98162718), // 456 + vec2(-0.20791169, 0.97814760), vec2(-0.22495105, 0.97437006), vec2(-0.24192190, 0.97029573), vec2(-0.25881905, 0.96592583), vec2(-0.27563736, 0.96126170), vec2(-0.29237170, 0.95630476), // 462 + vec2(-0.30901699, 0.95105652), vec2(-0.32556815, 0.94551858), vec2(-0.34202014, 0.93969262), vec2(-0.35836795, 0.93358043), vec2(-0.37460659, 0.92718385), vec2(-0.39073113, 0.92050485), // 468 + vec2(-0.40673664, 0.91354546), vec2(-0.42261826, 0.90630779), vec2(-0.43837115, 0.89879405), vec2(-0.45399050, 0.89100652), vec2(-0.46947156, 0.88294759), vec2(-0.48480962, 0.87461971), // 474 + vec2(-0.50000000, 0.86602540), vec2(-0.51503807, 0.85716730), vec2(-0.52991926, 0.84804810), vec2(-0.54463904, 0.83867057), vec2(-0.55919290, 0.82903757), vec2(-0.57357644, 0.81915204), // 480 + vec2(-0.58778525, 0.80901699), vec2(-0.60181502, 0.79863551), vec2(-0.61566148, 0.78801075), vec2(-0.62932039, 0.77714596), vec2(-0.64278761, 0.76604444), vec2(-0.65605903, 0.75470958), // 486 + vec2(-0.66913061, 0.74314483), vec2(-0.68199836, 0.73135370), vec2(-0.69465837, 0.71933980), vec2(-0.70710678, 0.70710678), vec2(-0.71933980, 0.69465837), vec2(-0.73135370, 0.68199836), // 492 + vec2(-0.74314483, 0.66913061), vec2(-0.75470958, 0.65605903), vec2(-0.76604444, 0.64278761), vec2(-0.77714596, 0.62932039), vec2(-0.78801075, 0.61566148), vec2(-0.79863551, 0.60181502), // 498 + vec2(-0.80901699, 0.58778525), vec2(-0.81915204, 0.57357644), vec2(-0.82903757, 0.55919290), vec2(-0.83867057, 0.54463904), vec2(-0.84804810, 0.52991926), vec2(-0.85716730, 0.51503807), // 504 + vec2(-0.86602540, 0.50000000), vec2(-0.87461971, 0.48480962), vec2(-0.88294759, 0.46947156), vec2(-0.89100652, 0.45399050), vec2(-0.89879405, 0.43837115), vec2(-0.90630779, 0.42261826), // 510 + vec2(-0.91354546, 0.40673664), vec2(-0.92050485, 0.39073113), vec2(-0.92718385, 0.37460659), vec2(-0.93358043, 0.35836795), vec2(-0.93969262, 0.34202014), vec2(-0.94551858, 0.32556815), // 516 + vec2(-0.95105652, 0.30901699), vec2(-0.95630476, 0.29237170), vec2(-0.96126170, 0.27563736), vec2(-0.96592583, 0.25881905), vec2(-0.97029573, 0.24192190), vec2(-0.97437006, 0.22495105), // 522 + vec2(-0.97814760, 0.20791169), vec2(-0.98162718, 0.19080900), vec2(-0.98480775, 0.17364818), vec2(-0.98768834, 0.15643447), vec2(-0.99026807, 0.13917310), vec2(-0.99254615, 0.12186934), // 528 + vec2(-0.99452190, 0.10452846), vec2(-0.99619470, 0.08715574), vec2(-0.99756405, 0.06975647), vec2(-0.99862953, 0.05233596), vec2(-0.99939083, 0.03489950), vec2(-0.99984770, 0.01745241), // 534 + vec2(-1.00000000, 0.00000000), vec2(-0.99984770, -0.01745241), vec2(-0.99939083, -0.03489950), vec2(-0.99862953, -0.05233596), vec2(-0.99756405, -0.06975647), vec2(-0.99619470, -0.08715574), // 540 + vec2(-0.99452190, -0.10452846), vec2(-0.99254615, -0.12186934), vec2(-0.99026807, -0.13917310), vec2(-0.98768834, -0.15643447), vec2(-0.98480775, -0.17364818), vec2(-0.98162718, -0.19080900), // 546 + vec2(-0.97814760, -0.20791169), vec2(-0.97437006, -0.22495105), vec2(-0.97029573, -0.24192190), vec2(-0.96592583, -0.25881905), vec2(-0.96126170, -0.27563736), vec2(-0.95630476, -0.29237170), // 552 + vec2(-0.95105652, -0.30901699), vec2(-0.94551858, -0.32556815), vec2(-0.93969262, -0.34202014), vec2(-0.93358043, -0.35836795), vec2(-0.92718385, -0.37460659), vec2(-0.92050485, -0.39073113), // 558 + vec2(-0.91354546, -0.40673664), vec2(-0.90630779, -0.42261826), vec2(-0.89879405, -0.43837115), vec2(-0.89100652, -0.45399050), vec2(-0.88294759, -0.46947156), vec2(-0.87461971, -0.48480962), // 564 + vec2(-0.86602540, -0.50000000), vec2(-0.85716730, -0.51503807), vec2(-0.84804810, -0.52991926), vec2(-0.83867057, -0.54463904), vec2(-0.82903757, -0.55919290), vec2(-0.81915204, -0.57357644), // 570 + vec2(-0.80901699, -0.58778525), vec2(-0.79863551, -0.60181502), vec2(-0.78801075, -0.61566148), vec2(-0.77714596, -0.62932039), vec2(-0.76604444, -0.64278761), vec2(-0.75470958, -0.65605903), // 576 + vec2(-0.74314483, -0.66913061), vec2(-0.73135370, -0.68199836), vec2(-0.71933980, -0.69465837), vec2(-0.70710678, -0.70710678), vec2(-0.69465837, -0.71933980), vec2(-0.68199836, -0.73135370), // 582 + vec2(-0.66913061, -0.74314483), vec2(-0.65605903, -0.75470958), vec2(-0.64278761, -0.76604444), vec2(-0.62932039, -0.77714596), vec2(-0.61566148, -0.78801075), vec2(-0.60181502, -0.79863551), // 588 + vec2(-0.58778525, -0.80901699), vec2(-0.57357644, -0.81915204), vec2(-0.55919290, -0.82903757), vec2(-0.54463904, -0.83867057), vec2(-0.52991926, -0.84804810), vec2(-0.51503807, -0.85716730), // 594 + vec2(-0.50000000, -0.86602540), vec2(-0.48480962, -0.87461971), vec2(-0.46947156, -0.88294759), vec2(-0.45399050, -0.89100652), vec2(-0.43837115, -0.89879405), vec2(-0.42261826, -0.90630779), // 600 + vec2(-0.40673664, -0.91354546), vec2(-0.39073113, -0.92050485), vec2(-0.37460659, -0.92718385), vec2(-0.35836795, -0.93358043), vec2(-0.34202014, -0.93969262), vec2(-0.32556815, -0.94551858), // 606 + vec2(-0.30901699, -0.95105652), vec2(-0.29237170, -0.95630476), vec2(-0.27563736, -0.96126170), vec2(-0.25881905, -0.96592583), vec2(-0.24192190, -0.97029573), vec2(-0.22495105, -0.97437006), // 612 + vec2(-0.20791169, -0.97814760), vec2(-0.19080900, -0.98162718), vec2(-0.17364818, -0.98480775), vec2(-0.15643447, -0.98768834), vec2(-0.13917310, -0.99026807), vec2(-0.12186934, -0.99254615), // 618 + vec2(-0.10452846, -0.99452190), vec2(-0.08715574, -0.99619470), vec2(-0.06975647, -0.99756405), vec2(-0.05233596, -0.99862953), vec2(-0.03489950, -0.99939083), vec2(-0.01745241, -0.99984770), // 624 + vec2(-0.00000000, -1.00000000), vec2(0.01745241, -0.99984770), vec2(0.03489950, -0.99939083), vec2(0.05233596, -0.99862953), vec2(0.06975647, -0.99756405), vec2(0.08715574, -0.99619470), // 630 + vec2(0.10452846, -0.99452190), vec2(0.12186934, -0.99254615), vec2(0.13917310, -0.99026807), vec2(0.15643447, -0.98768834), vec2(0.17364818, -0.98480775), vec2(0.19080900, -0.98162718), // 636 + vec2(0.20791169, -0.97814760), vec2(0.22495105, -0.97437006), vec2(0.24192190, -0.97029573), vec2(0.25881905, -0.96592583), vec2(0.27563736, -0.96126170), vec2(0.29237170, -0.95630476), // 642 + vec2(0.30901699, -0.95105652), vec2(0.32556815, -0.94551858), vec2(0.34202014, -0.93969262), vec2(0.35836795, -0.93358043), vec2(0.37460659, -0.92718385), vec2(0.39073113, -0.92050485), // 648 + vec2(0.40673664, -0.91354546), vec2(0.42261826, -0.90630779), vec2(0.43837115, -0.89879405), vec2(0.45399050, -0.89100652), vec2(0.46947156, -0.88294759), vec2(0.48480962, -0.87461971), // 654 + vec2(0.50000000, -0.86602540), vec2(0.51503807, -0.85716730), vec2(0.52991926, -0.84804810), vec2(0.54463904, -0.83867057), vec2(0.55919290, -0.82903757), vec2(0.57357644, -0.81915204), // 660 + vec2(0.58778525, -0.80901699), vec2(0.60181502, -0.79863551), vec2(0.61566148, -0.78801075), vec2(0.62932039, -0.77714596), vec2(0.64278761, -0.76604444), vec2(0.65605903, -0.75470958), // 666 + vec2(0.66913061, -0.74314483), vec2(0.68199836, -0.73135370), vec2(0.69465837, -0.71933980), vec2(0.70710678, -0.70710678), vec2(0.71933980, -0.69465837), vec2(0.73135370, -0.68199836), // 672 + vec2(0.74314483, -0.66913061), vec2(0.75470958, -0.65605903), vec2(0.76604444, -0.64278761), vec2(0.77714596, -0.62932039), vec2(0.78801075, -0.61566148), vec2(0.79863551, -0.60181502), // 678 + vec2(0.80901699, -0.58778525), vec2(0.81915204, -0.57357644), vec2(0.82903757, -0.55919290), vec2(0.83867057, -0.54463904), vec2(0.84804810, -0.52991926), vec2(0.85716730, -0.51503807), // 684 + vec2(0.86602540, -0.50000000), vec2(0.87461971, -0.48480962), vec2(0.88294759, -0.46947156), vec2(0.89100652, -0.45399050), vec2(0.89879405, -0.43837115), vec2(0.90630779, -0.42261826), // 690 + vec2(0.91354546, -0.40673664), vec2(0.92050485, -0.39073113), vec2(0.92718385, -0.37460659), vec2(0.93358043, -0.35836795), vec2(0.93969262, -0.34202014), vec2(0.94551858, -0.32556815), // 696 + vec2(0.95105652, -0.30901699), vec2(0.95630476, -0.29237170), vec2(0.96126170, -0.27563736), vec2(0.96592583, -0.25881905), vec2(0.97029573, -0.24192190), vec2(0.97437006, -0.22495105), // 702 + vec2(0.97814760, -0.20791169), vec2(0.98162718, -0.19080900), vec2(0.98480775, -0.17364818), vec2(0.98768834, -0.15643447), vec2(0.99026807, -0.13917310), vec2(0.99254615, -0.12186934), // 708 + vec2(0.99452190, -0.10452846), vec2(0.99619470, -0.08715574), vec2(0.99756405, -0.06975647), vec2(0.99862953, -0.05233596), vec2(0.99939083, -0.03489950), vec2(0.99984770, -0.01745241), // 714 + vec2(1.00000000, 0.00000000) // 720 +}; + diff --git a/src/shared/geom.h b/src/shared/geom.h new file mode 100644 index 0000000..3adccc6 --- /dev/null +++ b/src/shared/geom.h @@ -0,0 +1,1828 @@ +struct vec; +struct vec4; + +struct vec2 +{ + union + { + struct { float x, y; }; + float v[2]; + }; + + vec2() {} + vec2(float x, float y) : x(x), y(y) {} + explicit vec2(const vec &v); + explicit vec2(const vec4 &v); + + float &operator[](int i) { return v[i]; } + float operator[](int i) const { return v[i]; } + + bool operator==(const vec2 &o) const { return x == o.x && y == o.y; } + bool operator!=(const vec2 &o) const { return x != o.x || y != o.y; } + + bool iszero() const { return x==0 && y==0; } + float dot(const vec2 &o) const { return x*o.x + y*o.y; } + float squaredlen() const { return dot(*this); } + float magnitude() const { return sqrtf(squaredlen()); } + vec2 &normalize() { mul(1/magnitude()); return *this; } + vec2 &safenormalize() { float m = magnitude(); if(m) mul(1/m); return *this; } + float cross(const vec2 &o) const { return x*o.y - y*o.x; } + + vec2 &mul(float f) { x *= f; y *= f; return *this; } + vec2 &mul(const vec2 &o) { x *= o.x; y *= o.y; return *this; } + vec2 &square() { mul(*this); return *this; } + vec2 &div(float f) { x /= f; y /= f; return *this; } + vec2 &div(const vec2 &o) { x /= o.x; y /= o.y; return *this; } + vec2 &recip() { x = 1/x; y = 1/y; return *this; } + vec2 &add(float f) { x += f; y += f; return *this; } + vec2 &add(const vec2 &o) { x += o.x; y += o.y; return *this; } + vec2 &sub(float f) { x -= f; y -= f; return *this; } + vec2 &sub(const vec2 &o) { x -= o.x; y -= o.y; return *this; } + vec2 &neg() { x = -x; y = -y; return *this; } + vec2 &min(const vec2 &o) { x = ::min(x, o.x); y = ::min(y, o.y); return *this; } + vec2 &max(const vec2 &o) { x = ::max(x, o.x); y = ::max(y, o.y); return *this; } + vec2 &min(float f) { x = ::min(x, f); y = ::min(y, f); return *this; } + vec2 &max(float f) { x = ::max(x, f); y = ::max(y, f); return *this; } + vec2 &abs() { x = fabs(x); y = fabs(y); return *this; } + vec2 &clamp(float l, float h) { x = ::clamp(x, l, h); y = ::clamp(y, l, h); return *this; } + vec2 &reflect(const vec2 &n) { float k = 2*dot(n); x -= k*n.x; y -= k*n.y; return *this; } + vec2 &lerp(const vec2 &b, float t) { x += (b.x-x)*t; y += (b.y-y)*t; return *this; } + vec2 &lerp(const vec2 &a, const vec2 &b, float t) { x = a.x + (b.x-a.x)*t; y = a.y + (b.y-a.y)*t; return *this; } + template<class B> vec2 &madd(const vec2 &a, const B &b) { return add(vec2(a).mul(b)); } + template<class B> vec2 &msub(const vec2 &a, const B &b) { return sub(vec2(a).mul(b)); } +}; + +static inline bool htcmp(const vec2 &x, const vec2 &y) +{ + return x == y; +} + +static inline uint hthash(const vec2 &k) +{ + union { uint i; float f; } x, y; + x.f = k.x; y.f = k.y; + uint v = x.i^y.i; + return v + (v>>12); +} + +struct ivec; + +struct vec +{ + union + { + struct { float x, y, z; }; + struct { float r, g, b; }; + float v[3]; + }; + + vec() {} + explicit vec(int a) : x(a), y(a), z(a) {} + explicit vec(float a) : x(a), y(a), z(a) {} + vec(float a, float b, float c) : x(a), y(b), z(c) {} + explicit vec(int v[3]) : x(v[0]), y(v[1]), z(v[2]) {} + explicit vec(const float *v) : x(v[0]), y(v[1]), z(v[2]) {} + explicit vec(const vec2 &v, float z = 0) : x(v.x), y(v.y), z(z) {} + explicit vec(const vec4 &v); + explicit vec(const ivec &v); + + vec(float yaw, float pitch) : x(-sinf(yaw)*cosf(pitch)), y(cosf(yaw)*cosf(pitch)), z(sinf(pitch)) {} + + float &operator[](int i) { return v[i]; } + float operator[](int i) const { return v[i]; } + + vec &set(int i, float f) { v[i] = f; return *this; } + + bool operator==(const vec &o) const { return x == o.x && y == o.y && z == o.z; } + bool operator!=(const vec &o) const { return x != o.x || y != o.y || z != o.z; } + + bool iszero() const { return x==0 && y==0 && z==0; } + float squaredlen() const { return x*x + y*y + z*z; } + template<class T> float dot2(const T &o) const { return x*o.x + y*o.y; } + float dot(const vec &o) const { return x*o.x + y*o.y + z*o.z; } + float absdot(const vec &o) const { return fabs(x*o.x) + fabs(y*o.y) + fabs(z*o.z); } + vec &pow(float f) { x = ::pow(x, f); y = ::pow(y, f); z = ::pow(z, f); return *this; } + vec &exp() { x = ::exp(x); y = ::exp(y); z = ::exp(z); return *this; } + vec &exp2() { x = ::exp2(x); y = ::exp2(y); z = ::exp2(z); return *this; } + vec &sqrt() { x = sqrtf(x); y = sqrtf(y); z = sqrtf(z); return *this; } + vec &mul(const vec &o) { x *= o.x; y *= o.y; z *= o.z; return *this; } + vec &mul(float f) { x *= f; y *= f; z *= f; return *this; } + vec &square() { mul(*this); return *this; } + vec &div(const vec &o) { x /= o.x; y /= o.y; z /= o.z; return *this; } + vec &div(float f) { x /= f; y /= f; z /= f; return *this; } + vec &recip() { x = 1/x; y = 1/y; z = 1/z; return *this; } + vec &add(const vec &o) { x += o.x; y += o.y; z += o.z; return *this; } + vec &add(float f) { x += f; y += f; z += f; return *this; } + vec &add2(float f) { x += f; y += f; return *this; } + vec &addz(float f) { z += f; return *this; } + vec &sub(const vec &o) { x -= o.x; y -= o.y; z -= o.z; return *this; } + vec &sub(float f) { x -= f; y -= f; z -= f; return *this; } + vec &sub2(float f) { x -= f; y -= f; return *this; } + vec &subz(float f) { z -= f; return *this; } + vec &neg2() { x = -x; y = -y; return *this; } + vec &neg() { x = -x; y = -y; z = -z; return *this; } + vec &min(const vec &o) { x = ::min(x, o.x); y = ::min(y, o.y); z = ::min(z, o.z); return *this; } + vec &max(const vec &o) { x = ::max(x, o.x); y = ::max(y, o.y); z = ::max(z, o.z); return *this; } + vec &min(float f) { x = ::min(x, f); y = ::min(y, f); z = ::min(z, f); return *this; } + vec &max(float f) { x = ::max(x, f); y = ::max(y, f); z = ::max(z, f); return *this; } + vec &clamp(float f, float h) { x = ::clamp(x, f, h); y = ::clamp(y, f, h); z = ::clamp(z, f, h); return *this; } + vec &abs() { x = fabs(x); y = fabs(y); z = fabs(z); return *this; } + float magnitude2() const { return sqrtf(dot2(*this)); } + float magnitude() const { return sqrtf(squaredlen()); } + vec &normalize() { div(magnitude()); return *this; } + vec &safenormalize() { float m = magnitude(); if(m) div(m); return *this; } + bool isnormalized() const { float m = squaredlen(); return (m>0.99f && m<1.01f); } + float squaredist(const vec &e) const { return vec(*this).sub(e).squaredlen(); } + float dist(const vec &e) const { vec t; return dist(e, t); } + float dist(const vec &e, vec &t) const { t = *this; t.sub(e); return t.magnitude(); } + float dist2(const vec &o) const { float dx = x-o.x, dy = y-o.y; return sqrtf(dx*dx + dy*dy); } + bool reject(const vec &o, float r) { return x>o.x+r || x<o.x-r || y>o.y+r || y<o.y-r; } + template<class A, class B> + vec &cross(const A &a, const B &b) { x = a.y*b.z-a.z*b.y; y = a.z*b.x-a.x*b.z; z = a.x*b.y-a.y*b.x; return *this; } + vec &cross(const vec &o, const vec &a, const vec &b) { return cross(vec(a).sub(o), vec(b).sub(o)); } + float scalartriple(const vec &a, const vec &b) const { return x*(a.y*b.z-a.z*b.y) + y*(a.z*b.x-a.x*b.z) + z*(a.x*b.y-a.y*b.x); } + vec &reflectz(float rz) { z = 2*rz - z; return *this; } + vec &reflect(const vec &n) { float k = 2*dot(n); x -= k*n.x; y -= k*n.y; z -= k*n.z; return *this; } + vec &project(const vec &n) { float k = dot(n); x -= k*n.x; y -= k*n.y; z -= k*n.z; return *this; } + vec &projectxydir(const vec &n) { if(n.z) z = -(x*n.x/n.z + y*n.y/n.z); return *this; } + vec &projectxy(const vec &n) + { + float m = squaredlen(), k = dot(n); + projectxydir(n); + rescale(sqrtf(::max(m - k*k, 0.0f))); + return *this; + } + vec &projectxy(const vec &n, float threshold) + { + float m = squaredlen(), k = ::min(dot(n), threshold); + projectxydir(n); + rescale(sqrtf(::max(m - k*k, 0.0f))); + return *this; + } + vec &lerp(const vec &b, float t) { x += (b.x-x)*t; y += (b.y-y)*t; z += (b.z-z)*t; return *this; } + vec &lerp(const vec &a, const vec &b, float t) { x = a.x + (b.x-a.x)*t; y = a.y + (b.y-a.y)*t; z = a.z + (b.z-a.z)*t; return *this; } + template<class B> vec &madd(const vec &a, const B &b) { return add(vec(a).mul(b)); } + template<class B> vec &msub(const vec &a, const B &b) { return sub(vec(a).mul(b)); } + + vec &rescale(float k) + { + float mag = magnitude(); + if(mag > 1e-6f) mul(k / mag); + return *this; + } + + vec &rotate_around_z(float c, float s) { float rx = x, ry = y; x = c*rx-s*ry; y = c*ry+s*rx; return *this; } + vec &rotate_around_x(float c, float s) { float ry = y, rz = z; y = c*ry-s*rz; z = c*rz+s*ry; return *this; } + vec &rotate_around_y(float c, float s) { float rx = x, rz = z; x = c*rx+s*rz; z = c*rz-s*rx; return *this; } + + vec &rotate_around_z(float angle) { return rotate_around_z(cosf(angle), sinf(angle)); } + vec &rotate_around_x(float angle) { return rotate_around_x(cosf(angle), sinf(angle)); } + vec &rotate_around_y(float angle) { return rotate_around_y(cosf(angle), sinf(angle)); } + + vec &rotate_around_z(const vec2 &sc) { return rotate_around_z(sc.x, sc.y); } + vec &rotate_around_x(const vec2 &sc) { return rotate_around_x(sc.x, sc.y); } + vec &rotate_around_y(const vec2 &sc) { return rotate_around_y(sc.x, sc.y); } + + vec &rotate(float c, float s, const vec &d) + { + *this = vec(x*(d.x*d.x*(1-c)+c) + y*(d.x*d.y*(1-c)-d.z*s) + z*(d.x*d.z*(1-c)+d.y*s), + x*(d.y*d.x*(1-c)+d.z*s) + y*(d.y*d.y*(1-c)+c) + z*(d.y*d.z*(1-c)-d.x*s), + x*(d.x*d.z*(1-c)-d.y*s) + y*(d.y*d.z*(1-c)+d.x*s) + z*(d.z*d.z*(1-c)+c)); + return *this; + } + vec &rotate(float angle, const vec &d) { return rotate(cosf(angle), sinf(angle), d); } + vec &rotate(const vec2 &sc, const vec &d) { return rotate(sc.x, sc.y, d); } + + void orthogonal(const vec &d) + { + *this = fabs(d.x) > fabs(d.z) ? vec(-d.y, d.x, 0) : vec(0, -d.z, d.y); + } + + void orthonormalize(vec &s, vec &t) const + { + s.sub(vec(*this).mul(dot(s))); + t.sub(vec(*this).mul(dot(t))) + .sub(vec(s).mul(s.dot(t))); + } + + template<class T> + bool insidebb(const T &bbmin, const T &bbmax) const + { + return x >= bbmin.x && x <= bbmax.x && y >= bbmin.y && y <= bbmax.y && z >= bbmin.z && z <= bbmax.z; + } + + template<class T, class U> + bool insidebb(const T &o, U size) const + { + return x >= o.x && x <= o.x + size && y >= o.y && y <= o.y + size && z >= o.z && z <= o.z + size; + } + + template<class T> float dist_to_bb(const T &min, const T &max) const + { + float sqrdist = 0; + loopi(3) + { + if (v[i] < min[i]) { float delta = v[i]-min[i]; sqrdist += delta*delta; } + else if(v[i] > max[i]) { float delta = max[i]-v[i]; sqrdist += delta*delta; } + } + return sqrtf(sqrdist); + } + + template<class T, class S> float dist_to_bb(const T &o, S size) const + { + return dist_to_bb(o, T(o).add(size)); + } + + static vec hexcolor(int color) + { + return vec(((color>>16)&0xFF)*(1.0f/255.0f), ((color>>8)&0xFF)*(1.0f/255.0f), (color&0xFF)*(1.0f/255.0f)); + } + int tohexcolor() const { return (int(::clamp(r, 0.0f, 1.0f)*255)<<16)|(int(::clamp(g, 0.0f, 1.0f)*255)<<8)|int(::clamp(b, 0.0f, 1.0f)*255); } +}; + +inline vec2::vec2(const vec &v) : x(v.x), y(v.y) {} + +static inline bool htcmp(const vec &x, const vec &y) +{ + return x == y; +} + +static inline uint hthash(const vec &k) +{ + union { uint i; float f; } x, y, z; + x.f = k.x; y.f = k.y; z.f = k.z; + uint v = x.i^y.i^z.i; + return v + (v>>12); +} + +struct vec4 +{ + union + { + struct { float x, y, z, w; }; + struct { float r, g, b, a; }; + float v[4]; + }; + + vec4() {} + explicit vec4(const vec &p, float w = 0) : x(p.x), y(p.y), z(p.z), w(w) {} + vec4(float x, float y, float z, float w) : x(x), y(y), z(z), w(w) {} + explicit vec4(const float *v) : x(v[0]), y(v[1]), z(v[2]), w(v[3]) {} + + float &operator[](int i) { return v[i]; } + float operator[](int i) const { return v[i]; } + + template<class T> float dot3(const T &o) const { return x*o.x + y*o.y + z*o.z; } + float dot(const vec4 &o) const { return dot3(o) + w*o.w; } + float dot(const vec &o) const { return x*o.x + y*o.y + z*o.z + w; } + float squaredlen() const { return dot(*this); } + float magnitude() const { return sqrtf(squaredlen()); } + float magnitude3() const { return sqrtf(dot3(*this)); } + vec4 &normalize() { mul(1/magnitude()); return *this; } + vec4 &safenormalize() { float m = magnitude(); if(m) mul(1/m); return *this; } + + vec4 &lerp(const vec4 &b, float t) + { + x += (b.x-x)*t; + y += (b.y-y)*t; + z += (b.z-z)*t; + w += (b.w-w)*t; + return *this; + } + vec4 &lerp(const vec4 &a, const vec4 &b, float t) + { + x = a.x+(b.x-a.x)*t; + y = a.y+(b.y-a.y)*t; + z = a.z+(b.z-a.z)*t; + w = a.w+(b.w-a.w)*t; + return *this; + } + + vec4 &mul3(float f) { x *= f; y *= f; z *= f; return *this; } + vec4 &mul(float f) { mul3(f); w *= f; return *this; } + vec4 &mul(const vec4 &o) { x *= o.x; y *= o.y; z *= o.z; w *= o.w; return *this; } + vec4 &square() { mul(*this); return *this; } + vec4 &div3(float f) { x /= f; y /= f; z /= f; return *this; } + vec4 &div(float f) { div3(f); w /= f; return *this; } + vec4 &div(const vec4 &o) { x /= o.x; y /= o.y; z /= o.z; w /= o.w; return *this; } + vec4 &recip() { x = 1/x; y = 1/y; z = 1/z; w = 1/w; return *this; } + vec4 &add(const vec4 &o) { x += o.x; y += o.y; z += o.z; w += o.w; return *this; } + vec4 &addw(float f) { w += f; return *this; } + vec4 &sub(const vec4 &o) { x -= o.x; y -= o.y; z -= o.z; w -= o.w; return *this; } + vec4 &subw(float f) { w -= f; return *this; } + vec4 &neg3() { x = -x; y = -y; z = -z; return *this; } + vec4 &neg() { neg3(); w = -w; return *this; } + template<class B> vec4 &madd(const vec4 &a, const B &b) { return add(vec4(a).mul(b)); } + template<class B> vec4 &msub(const vec4 &a, const B &b) { return sub(vec4(a).mul(b)); } + + void setxyz(const vec &v) { x = v.x; y = v.y; z = v.z; } + + vec4 &rotate_around_z(float c, float s) { float rx = x, ry = y; x = c*rx-s*ry; y = c*ry+s*rx; return *this; } + vec4 &rotate_around_x(float c, float s) { float ry = y, rz = z; y = c*ry-s*rz; z = c*rz+s*ry; return *this; } + vec4 &rotate_around_y(float c, float s) { float rx = x, rz = z; x = c*rx+s*rz; z = c*rz-s*rx; return *this; } + + vec4 &rotate_around_z(float angle) { return rotate_around_z(cosf(angle), sinf(angle)); } + vec4 &rotate_around_x(float angle) { return rotate_around_x(cosf(angle), sinf(angle)); } + vec4 &rotate_around_y(float angle) { return rotate_around_y(cosf(angle), sinf(angle)); } +}; + +inline vec::vec(const vec4 &v) : x(v.x), y(v.y), z(v.z) {} + +struct matrix3; +struct matrix4x3; +struct matrix4; + +struct quat : vec4 +{ + quat() {} + quat(float x, float y, float z, float w) : vec4(x, y, z, w) {} + quat(const vec &axis, float angle) + { + w = cosf(angle/2); + float s = sinf(angle/2); + x = s*axis.x; + y = s*axis.y; + z = s*axis.z; + } + explicit quat(const vec &v) + { + x = v.x; + y = v.y; + z = v.z; + restorew(); + } + explicit quat(const matrix3 &m) { convertmatrix(m); } + explicit quat(const matrix4x3 &m) { convertmatrix(m); } + explicit quat(const matrix4 &m) { convertmatrix(m); } + + void restorew() { w = 1.0f-x*x-y*y-z*z; w = w<0 ? 0 : -sqrtf(w); } + + quat &add(const vec4 &o) { vec4::add(o); return *this; } + quat &sub(const vec4 &o) { vec4::sub(o); return *this; } + quat &mul(float k) { vec4::mul(k); return *this; } + + quat &mul(const quat &p, const quat &o) + { + x = p.w*o.x + p.x*o.w + p.y*o.z - p.z*o.y; + y = p.w*o.y - p.x*o.z + p.y*o.w + p.z*o.x; + z = p.w*o.z + p.x*o.y - p.y*o.x + p.z*o.w; + w = p.w*o.w - p.x*o.x - p.y*o.y - p.z*o.z; + return *this; + } + quat &mul(const quat &o) { return mul(quat(*this), o); } + + quat &invert() { neg3(); return *this; } + + void calcangleaxis(float &angle, vec &axis) + { + float rr = dot3(*this); + if(rr>0) + { + angle = 2*acosf(w); + axis = vec(x, y, z).mul(1/rr); + } + else { angle = 0; axis = vec(0, 0, 1); } + } + + vec rotate(const vec &v) const + { + return vec().cross(*this, vec().cross(*this, v).add(vec(v).mul(w))).mul(2).add(v); + } + + vec invertedrotate(const vec &v) const + { + return vec().cross(*this, vec().cross(*this, v).sub(vec(v).mul(w))).mul(2).add(v); + } + + template<class M> + void convertmatrix(const M &m) + { + float trace = m.a.x + m.b.y + m.c.z; + if(trace>0) + { + float r = sqrtf(1 + trace), inv = 0.5f/r; + w = 0.5f*r; + x = (m.b.z - m.c.y)*inv; + y = (m.c.x - m.a.z)*inv; + z = (m.a.y - m.b.x)*inv; + } + else if(m.a.x > m.b.y && m.a.x > m.c.z) + { + float r = sqrtf(1 + m.a.x - m.b.y - m.c.z), inv = 0.5f/r; + x = 0.5f*r; + y = (m.a.y + m.b.x)*inv; + z = (m.c.x + m.a.z)*inv; + w = (m.b.z - m.c.y)*inv; + } + else if(m.b.y > m.c.z) + { + float r = sqrtf(1 + m.b.y - m.a.x - m.c.z), inv = 0.5f/r; + x = (m.a.y + m.b.x)*inv; + y = 0.5f*r; + z = (m.b.z + m.c.y)*inv; + w = (m.c.x - m.a.z)*inv; + } + else + { + float r = sqrtf(1 + m.c.z - m.a.x - m.b.y), inv = 0.5f/r; + x = (m.c.x + m.a.z)*inv; + y = (m.b.z + m.c.y)*inv; + z = 0.5f*r; + w = (m.a.y - m.b.x)*inv; + } + } +}; + +struct dualquat +{ + quat real, dual; + + dualquat() {} + dualquat(const quat &q, const vec &p) + : real(q), + dual(0.5f*( p.x*q.w + p.y*q.z - p.z*q.y), + 0.5f*(-p.x*q.z + p.y*q.w + p.z*q.x), + 0.5f*( p.x*q.y - p.y*q.x + p.z*q.w), + -0.5f*( p.x*q.x + p.y*q.y + p.z*q.z)) + { + } + explicit dualquat(const quat &q) : real(q), dual(0, 0, 0, 0) {} + explicit dualquat(const matrix4x3 &m); + + dualquat &mul(float k) { real.mul(k); dual.mul(k); return *this; } + dualquat &add(const dualquat &d) { real.add(d.real); dual.add(d.dual); return *this; } + + dualquat &lerp(const dualquat &to, float t) + { + float k = real.dot(to.real) < 0 ? -t : t; + real.mul(1-t).add(vec4(to.real).mul(k)); + dual.mul(1-t).add(vec4(to.dual).mul(k)); + return *this; + } + dualquat &lerp(const dualquat &from, const dualquat &to, float t) + { + float k = from.real.dot(to.real) < 0 ? -t : t; + (real = from.real).mul(1-t).add(vec4(to.real).mul(k)); + (dual = from.dual).mul(1-t).add(vec4(to.dual).mul(k)); + return *this; + } + + dualquat &invert() + { + real.invert(); + dual.invert(); + dual.sub(quat(real).mul(2*real.dot(dual))); + return *this; + } + + void mul(const dualquat &p, const dualquat &o) + { + real.mul(p.real, o.real); + dual.mul(p.real, o.dual).add(quat().mul(p.dual, o.real)); + } + void mul(const dualquat &o) { mul(dualquat(*this), o); } + + void mulorient(const quat &q) + { + real.mul(q, quat(real)); + dual.mul(quat(q).invert(), quat(dual)); + } + + void mulorient(const quat &q, const dualquat &base) + { + quat trans; + trans.mul(base.dual, quat(base.real).invert()); + dual.mul(quat(q).invert(), quat(real).mul(trans).add(dual)); + + real.mul(q, quat(real)); + dual.add(quat().mul(real, trans.invert())).sub(quat(real).mul(2*base.real.dot(base.dual))); + } + + void normalize() + { + float invlen = 1/real.magnitude(); + real.mul(invlen); + dual.mul(invlen); + } + + void translate(const vec &p) + { + dual.x += 0.5f*( p.x*real.w + p.y*real.z - p.z*real.y); + dual.y += 0.5f*(-p.x*real.z + p.y*real.w + p.z*real.x); + dual.z += 0.5f*( p.x*real.y - p.y*real.x + p.z*real.w); + dual.w += -0.5f*( p.x*real.x + p.y*real.y + p.z*real.z); + } + + void scale(float k) + { + dual.mul(k); + } + + void fixantipodal(const dualquat &d) + { + if(real.dot(d.real) < 0) + { + real.neg(); + dual.neg(); + } + } + + void accumulate(const dualquat &d, float k) + { + if(real.dot(d.real) < 0) k = -k; + real.add(vec4(d.real).mul(k)); + dual.add(vec4(d.dual).mul(k)); + } + + vec transform(const vec &v) const + { + return vec().cross(real, vec().cross(real, v).add(vec(v).mul(real.w)).add(vec(dual))).add(vec(dual).mul(real.w)).sub(vec(real).mul(dual.w)).mul(2).add(v); + } + + quat transform(const quat &q) const + { + return quat().mul(real, q); + } + + vec transposedtransform(const vec &v) const + { + return dualquat(*this).invert().transform(v); + } + + vec transformnormal(const vec &v) const + { + return real.rotate(v); + } + + vec transposedtransformnormal(const vec &v) const + { + return real.invertedrotate(v); + } + + vec gettranslation() const + { + return vec().cross(real, dual).add(vec(dual).mul(real.w)).sub(vec(real).mul(dual.w)).mul(2); + } +}; + +struct matrix3 +{ + vec a, b, c; + + matrix3() {} + matrix3(const vec &a, const vec &b, const vec &c) : a(a), b(b), c(c) {} + explicit matrix3(float angle, const vec &axis) { rotate(angle, axis); } + explicit matrix3(const quat &q) + { + float x = q.x, y = q.y, z = q.z, w = q.w, + tx = 2*x, ty = 2*y, tz = 2*z, + txx = tx*x, tyy = ty*y, tzz = tz*z, + txy = tx*y, txz = tx*z, tyz = ty*z, + twx = w*tx, twy = w*ty, twz = w*tz; + a = vec(1 - (tyy + tzz), txy + twz, txz - twy); + b = vec(txy - twz, 1 - (txx + tzz), tyz + twx); + c = vec(txz + twy, tyz - twx, 1 - (txx + tyy)); + } + explicit matrix3(const matrix4x3 &m); + explicit matrix3(const matrix4 &m); + + void mul(const matrix3 &m, const matrix3 &n) + { + a = vec(m.a).mul(n.a.x).madd(m.b, n.a.y).madd(m.c, n.a.z); + b = vec(m.a).mul(n.b.x).madd(m.b, n.b.y).madd(m.c, n.b.z); + c = vec(m.a).mul(n.c.x).madd(m.b, n.c.y).madd(m.c, n.c.z); + } + void mul(const matrix3 &n) { mul(matrix3(*this), n); } + + void multranspose(const matrix3 &m, const matrix3 &n) + { + a = vec(m.a).mul(n.a.x).madd(m.b, n.b.x).madd(m.c, n.c.x); + b = vec(m.a).mul(n.a.y).madd(m.b, n.b.y).madd(m.c, n.c.y); + c = vec(m.a).mul(n.a.z).madd(m.b, n.b.z).madd(m.c, n.c.z); + } + void multranspose(const matrix3 &n) { multranspose(matrix3(*this), n); } + + void transposemul(const matrix3 &m, const matrix3 &n) + { + a = vec(m.a.dot(n.a), m.b.dot(n.a), m.c.dot(n.a)); + b = vec(m.a.dot(n.b), m.b.dot(n.b), m.c.dot(n.b)); + c = vec(m.a.dot(n.c), m.b.dot(n.c), m.c.dot(n.c)); + } + void transposemul(const matrix3 &n) { transposemul(matrix3(*this), n); } + + void transpose() + { + swap(a.y, b.x); swap(a.z, c.x); + swap(b.z, c.y); + } + + template<class M> + void transpose(const M &m) + { + a = vec(m.a.x, m.b.x, m.c.x); + b = vec(m.a.y, m.b.y, m.c.y); + c = vec(m.a.z, m.b.z, m.c.z); + } + + void invert(const matrix3 &o) + { + vec unscale(1/o.a.squaredlen(), 1/o.b.squaredlen(), 1/o.c.squaredlen()); + transpose(o); + a.mul(unscale); + b.mul(unscale); + c.mul(unscale); + } + void invert() { invert(matrix3(*this)); } + + void normalize() + { + a.normalize(); + b.normalize(); + c.normalize(); + } + + void scale(float k) + { + a.mul(k); + b.mul(k); + c.mul(k); + } + + void rotate(float angle, const vec &axis) + { + rotate(cosf(angle), sinf(angle), axis); + } + + void rotate(float ck, float sk, const vec &axis) + { + a = vec(axis.x*axis.x*(1-ck)+ck, axis.x*axis.y*(1-ck)+axis.z*sk, axis.x*axis.z*(1-ck)-axis.y*sk); + b = vec(axis.x*axis.y*(1-ck)-axis.z*sk, axis.y*axis.y*(1-ck)+ck, axis.y*axis.z*(1-ck)+axis.x*sk); + c = vec(axis.x*axis.z*(1-ck)+axis.y*sk, axis.y*axis.z*(1-ck)-axis.x*sk, axis.z*axis.z*(1-ck)+ck); + } + + void setyaw(float ck, float sk) + { + a = vec(ck, sk, 0); + b = vec(-sk, ck, 0); + c = vec(0, 0, 1); + } + + void setyaw(float angle) + { + setyaw(cosf(angle), sinf(angle)); + } + + float trace() const { return a.x + b.y + c.z; } + + bool calcangleaxis(float tr, float &angle, vec &axis, float threshold = 1e-16f) const + { + if(tr <= -1) + { + if(a.x >= b.y && a.x >= c.z) + { + float r = 1 + a.x - b.y - c.z; + if(r <= threshold) return false; + r = sqrtf(r); + axis.x = 0.5f*r; + axis.y = b.x/r; + axis.z = c.x/r; + } + else if(b.y >= c.z) + { + float r = 1 + b.y - a.x - c.z; + if(r <= threshold) return false; + r = sqrtf(r); + axis.y = 0.5f*r; + axis.x = b.x/r; + axis.z = c.y/r; + } + else + { + float r = 1 + b.y - a.x - c.z; + if(r <= threshold) return false; + r = sqrtf(r); + axis.z = 0.5f*r; + axis.x = c.x/r; + axis.y = c.y/r; + } + angle = M_PI; + } + else if(tr >= 3) + { + axis = vec(0, 0, 1); + angle = 0; + } + else + { + axis = vec(b.z - c.y, c.x - a.z, a.y - b.x); + float r = axis.squaredlen(); + if(r <= threshold) return false; + axis.mul(1/sqrtf(r)); + angle = acosf(0.5f*(tr - 1)); + } + return true; + } + + bool calcangleaxis(float &angle, vec &axis, float threshold = 1e-16f) const { return calcangleaxis(trace(), angle, axis, threshold); } + + vec transform(const vec &o) const + { + return vec(a).mul(o.x).madd(b, o.y).madd(c, o.z); + } + vec transposedtransform(const vec &o) const { return vec(a.dot(o), b.dot(o), c.dot(o)); } + vec abstransform(const vec &o) const + { + return vec(a).mul(o.x).abs().add(vec(b).mul(o.y).abs()).add(vec(c).mul(o.z).abs()); + } + vec abstransposedtransform(const vec &o) const + { + return vec(a.absdot(o), b.absdot(o), c.absdot(o)); + } + + void identity() + { + a = vec(1, 0, 0); + b = vec(0, 1, 0); + c = vec(0, 0, 1); + } + + void rotate_around_x(float ck, float sk) + { + vec rb = vec(b).mul(ck).madd(c, sk), + rc = vec(c).mul(ck).msub(b, sk); + b = rb; + c = rc; + } + void rotate_around_x(float angle) { rotate_around_x(cosf(angle), sinf(angle)); } + void rotate_around_x(const vec2 &sc) { rotate_around_x(sc.x, sc.y); } + + void rotate_around_y(float ck, float sk) + { + vec rc = vec(c).mul(ck).madd(a, sk), + ra = vec(a).mul(ck).msub(c, sk); + c = rc; + a = ra; + } + void rotate_around_y(float angle) { rotate_around_y(cosf(angle), sinf(angle)); } + void rotate_around_y(const vec2 &sc) { rotate_around_y(sc.x, sc.y); } + + void rotate_around_z(float ck, float sk) + { + vec ra = vec(a).mul(ck).madd(b, sk), + rb = vec(b).mul(ck).msub(a, sk); + a = ra; + b = rb; + } + void rotate_around_z(float angle) { rotate_around_z(cosf(angle), sinf(angle)); } + void rotate_around_z(const vec2 &sc) { rotate_around_z(sc.x, sc.y); } + + vec transform(const vec2 &o) { return vec(a).mul(o.x).madd(b, o.y); } + vec transposedtransform(const vec2 &o) const { return vec(a.dot2(o), b.dot2(o), c.dot2(o)); } + + vec rowx() const { return vec(a.x, b.x, c.x); } + vec rowy() const { return vec(a.y, b.y, c.y); } + vec rowz() const { return vec(a.z, b.z, c.z); } +}; + +struct matrix4x3 +{ + vec a, b, c, d; + + matrix4x3() {} + matrix4x3(const vec &a, const vec &b, const vec &c, const vec &d) : a(a), b(b), c(c), d(d) {} + matrix4x3(const matrix3 &rot, const vec &trans) : a(rot.a), b(rot.b), c(rot.c), d(trans) {} + matrix4x3(const dualquat &dq) + { + vec4 r = vec4(dq.real).mul(1/dq.real.squaredlen()), rr = vec4(r).mul(dq.real); + r.mul(2); + float xy = r.x*dq.real.y, xz = r.x*dq.real.z, yz = r.y*dq.real.z, + wx = r.w*dq.real.x, wy = r.w*dq.real.y, wz = r.w*dq.real.z; + a = vec(rr.w + rr.x - rr.y - rr.z, xy + wz, xz - wy); + b = vec(xy - wz, rr.w + rr.y - rr.x - rr.z, yz + wx); + c = vec(xz + wy, yz - wx, rr.w + rr.z - rr.x - rr.y); + d = vec(-(dq.dual.w*r.x - dq.dual.x*r.w + dq.dual.y*r.z - dq.dual.z*r.y), + -(dq.dual.w*r.y - dq.dual.x*r.z - dq.dual.y*r.w + dq.dual.z*r.x), + -(dq.dual.w*r.z + dq.dual.x*r.y - dq.dual.y*r.x - dq.dual.z*r.w)); + + } + explicit matrix4x3(const matrix4 &m); + + void mul(float k) + { + a.mul(k); + b.mul(k); + c.mul(k); + d.mul(k); + } + + void setscale(float x, float y, float z) { a.x = x; b.y = y; c.z = z; } + void setscale(const vec &v) { setscale(v.x, v.y, v.z); } + void setscale(float n) { setscale(n, n, n); } + + void scale(float x, float y, float z) + { + a.mul(x); + b.mul(y); + c.mul(z); + } + void scale(const vec &v) { scale(v.x, v.y, v.z); } + void scale(float n) { scale(n, n, n); } + + void settranslation(const vec &p) { d = p; } + void settranslation(float x, float y, float z) { d = vec(x, y, z); } + + void translate(const vec &p) { d.madd(a, p.x).madd(b, p.y).madd(c, p.z); } + void translate(float x, float y, float z) { translate(vec(x, y, z)); } + void translate(const vec &p, float scale) { translate(vec(p).mul(scale)); } + + void posttranslate(const vec &p) { d.add(p); } + void posttranslate(float x, float y, float z) { posttranslate(vec(x, y, z)); } + void posttranslate(const vec &p, float scale) { d.madd(p, scale); } + + void accumulate(const matrix4x3 &m, float k) + { + a.madd(m.a, k); + b.madd(m.b, k); + c.madd(m.c, k); + d.madd(m.d, k); + } + + void normalize() + { + a.normalize(); + b.normalize(); + c.normalize(); + } + + void lerp(const matrix4x3 &to, float t) + { + a.lerp(to.a, t); + b.lerp(to.b, t); + c.lerp(to.c, t); + d.lerp(to.d, t); + } + void lerp(const matrix4x3 &from, const matrix4x3 &to, float t) + { + a.lerp(from.a, to.a, t); + b.lerp(from.b, to.b, t); + c.lerp(from.c, to.c, t); + d.lerp(from.d, to.d, t); + } + + void identity() + { + a = vec(1, 0, 0); + b = vec(0, 1, 0); + c = vec(0, 0, 1); + d = vec(0, 0, 0); + } + + void mul(const matrix4x3 &m, const matrix4x3 &n) + { + a = vec(m.a).mul(n.a.x).madd(m.b, n.a.y).madd(m.c, n.a.z); + b = vec(m.a).mul(n.b.x).madd(m.b, n.b.y).madd(m.c, n.b.z); + c = vec(m.a).mul(n.c.x).madd(m.b, n.c.y).madd(m.c, n.c.z); + d = vec(m.d).madd(m.a, n.d.x).madd(m.b, n.d.y).madd(m.c, n.d.z); + } + void mul(const matrix4x3 &n) { mul(matrix4x3(*this), n); } + + void mul(const matrix3 &m, const matrix4x3 &n) + { + a = vec(m.a).mul(n.a.x).madd(m.b, n.a.y).madd(m.c, n.a.z); + b = vec(m.a).mul(n.b.x).madd(m.b, n.b.y).madd(m.c, n.b.z); + c = vec(m.a).mul(n.c.x).madd(m.b, n.c.y).madd(m.c, n.c.z); + d = vec(m.a).mul(n.d.x).madd(m.b, n.d.y).madd(m.c, n.d.z); + } + + void mul(const matrix3 &rot, const vec &trans, const matrix4x3 &n) + { + mul(rot, n); + d.add(trans); + } + + void transpose() + { + d = vec(a.dot(d), b.dot(d), c.dot(d)).neg(); + swap(a.y, b.x); swap(a.z, c.x); + swap(b.z, c.y); + } + + void transpose(const matrix4x3 &o) + { + a = vec(o.a.x, o.b.x, o.c.x); + b = vec(o.a.y, o.b.y, o.c.y); + c = vec(o.a.z, o.b.z, o.c.z); + d = vec(o.a.dot(o.d), o.b.dot(o.d), o.c.dot(o.d)).neg(); + } + + void transposemul(const matrix4x3 &m, const matrix4x3 &n) + { + vec t(m.a.dot(m.d), m.b.dot(m.d), m.c.dot(m.d)); + a = vec(m.a.dot(n.a), m.b.dot(n.a), m.c.dot(n.a)); + b = vec(m.a.dot(n.b), m.b.dot(n.b), m.c.dot(n.b)); + c = vec(m.a.dot(n.c), m.b.dot(n.c), m.c.dot(n.c)); + d = vec(m.a.dot(n.d), m.b.dot(n.d), m.c.dot(n.d)).sub(t); + } + + void multranspose(const matrix4x3 &m, const matrix4x3 &n) + { + vec t(n.a.dot(n.d), n.b.dot(n.d), n.c.dot(n.d)); + a = vec(m.a).mul(n.a.x).madd(m.b, n.b.x).madd(m.c, n.c.x); + b = vec(m.a).mul(n.a.y).madd(m.b, n.b.y).madd(m.c, n.c.y); + c = vec(m.a).mul(n.a.z).madd(m.b, n.b.z).madd(m.c, n.c.z); + d = vec(m.d).msub(m.a, t.x).msub(m.b, t.y).msub(m.c, t.z); + } + + void invert(const matrix4x3 &o) + { + vec unscale(1/o.a.squaredlen(), 1/o.b.squaredlen(), 1/o.c.squaredlen()); + transpose(o); + a.mul(unscale); + b.mul(unscale); + c.mul(unscale); + d.mul(unscale); + } + void invert() { invert(matrix4x3(*this)); } + + void rotate(float angle, const vec &d) + { + rotate(cosf(angle), sinf(angle), d); + } + + void rotate(float ck, float sk, const vec &axis) + { + matrix3 m; + m.rotate(ck, sk, axis); + *this = matrix4x3(m, vec(0, 0, 0)); + } + + void rotate_around_x(float ck, float sk) + { + vec rb = vec(b).mul(ck).madd(c, sk), + rc = vec(c).mul(ck).msub(b, sk); + b = rb; + c = rc; + } + void rotate_around_x(float angle) { rotate_around_x(cosf(angle), sinf(angle)); } + void rotate_around_x(const vec2 &sc) { rotate_around_x(sc.x, sc.y); } + + void rotate_around_y(float ck, float sk) + { + vec rc = vec(c).mul(ck).madd(a, sk), + ra = vec(a).mul(ck).msub(c, sk); + c = rc; + a = ra; + } + void rotate_around_y(float angle) { rotate_around_y(cosf(angle), sinf(angle)); } + void rotate_around_y(const vec2 &sc) { rotate_around_y(sc.x, sc.y); } + + void rotate_around_z(float ck, float sk) + { + vec ra = vec(a).mul(ck).madd(b, sk), + rb = vec(b).mul(ck).msub(a, sk); + a = ra; + b = rb; + } + void rotate_around_z(float angle) { rotate_around_z(cosf(angle), sinf(angle)); } + void rotate_around_z(const vec2 &sc) { rotate_around_z(sc.x, sc.y); } + + vec transform(const vec &o) const { return vec(d).madd(a, o.x).madd(b, o.y).madd(c, o.z); } + vec transposedtransform(const vec &o) const { vec p = vec(o).sub(d); return vec(a.dot(p), b.dot(p), c.dot(p)); } + vec transformnormal(const vec &o) const { return vec(a).mul(o.x).madd(b, o.y).madd(c, o.z); } + vec transposedtransformnormal(const vec &o) const { return vec(a.dot(o), b.dot(o), c.dot(o)); } + vec transform(const vec2 &o) const { return vec(d).madd(a, o.x).madd(b, o.y); } + + vec4 rowx() const { return vec4(a.x, b.x, c.x, d.x); } + vec4 rowy() const { return vec4(a.y, b.y, c.y, d.y); } + vec4 rowz() const { return vec4(a.z, b.z, c.z, d.z); } +}; + +inline dualquat::dualquat(const matrix4x3 &m) : real(m) +{ + dual.x = 0.5f*( m.d.x*real.w + m.d.y*real.z - m.d.z*real.y); + dual.y = 0.5f*(-m.d.x*real.z + m.d.y*real.w + m.d.z*real.x); + dual.z = 0.5f*( m.d.x*real.y - m.d.y*real.x + m.d.z*real.w); + dual.w = -0.5f*( m.d.x*real.x + m.d.y*real.y + m.d.z*real.z); +} + +inline matrix3::matrix3(const matrix4x3 &m) : a(m.a), b(m.b), c(m.c) {} + +struct plane : vec +{ + float offset; + + float dist(const vec &p) const { return dot(p)+offset; } + float dist(const vec4 &p) const { return p.dot3(*this) + p.w*offset; } + bool operator==(const plane &p) const { return x==p.x && y==p.y && z==p.z && offset==p.offset; } + bool operator!=(const plane &p) const { return x!=p.x || y!=p.y || z!=p.z || offset!=p.offset; } + + plane() {} + plane(const vec &c, float off) : vec(c), offset(off) {} + plane(const vec4 &p) : vec(p), offset(p.w) {} + plane(int d, float off) + { + x = y = z = 0.0f; + v[d] = 1.0f; + offset = -off; + } + plane(float a, float b, float c, float d) : vec(a, b, c), offset(d) {} + + void toplane(const vec &n, const vec &p) + { + x = n.x; y = n.y; z = n.z; + offset = -dot(p); + } + + bool toplane(const vec &a, const vec &b, const vec &c) + { + cross(vec(b).sub(a), vec(c).sub(a)); + float mag = magnitude(); + if(!mag) return false; + div(mag); + offset = -dot(a); + return true; + } + + bool rayintersect(const vec &o, const vec &ray, float &dist) + { + float cosalpha = dot(ray); + if(cosalpha==0) return false; + float deltac = offset+dot(o); + dist -= deltac/cosalpha; + return true; + } + + plane &reflectz(float rz) + { + offset += 2*rz*z; + z = -z; + return *this; + } + + plane &invert() + { + neg(); + offset = -offset; + return *this; + } + + plane &scale(float k) + { + mul(k); + return *this; + } + + plane &translate(const vec &p) + { + offset += dot(p); + return *this; + } + + plane &normalize() + { + float mag = magnitude(); + div(mag); + offset /= mag; + return *this; + } + + float zintersect(const vec &p) const { return -(x*p.x+y*p.y+offset)/z; } + float zdelta(const vec &p) const { return -(x*p.x+y*p.y)/z; } + float zdist(const vec &p) const { return p.z-zintersect(p); } +}; + +struct triangle +{ + vec a, b, c; + + triangle(const vec &a, const vec &b, const vec &c) : a(a), b(b), c(c) {} + triangle() {} + + triangle &add(const vec &o) { a.add(o); b.add(o); c.add(o); return *this; } + triangle &sub(const vec &o) { a.sub(o); b.sub(o); c.sub(o); return *this; } + + bool operator==(const triangle &t) const { return a == t.a && b == t.b && c == t.c; } +}; + +/** + +Sauerbraten uses 3 different linear coordinate systems +which are oriented around each of the axis dimensions. + +So any point within the game can be defined by four coordinates: (d, x, y, z) + +d is the reference axis dimension +x is the coordinate of the ROW dimension +y is the coordinate of the COL dimension +z is the coordinate of the reference dimension (DEPTH) + +typically, if d is not used, then it is implicitly the Z dimension. +ie: d=z => x=x, y=y, z=z + +**/ + +// DIM: X=0 Y=1 Z=2. +const int R[3] = {1, 2, 0}; // row +const int C[3] = {2, 0, 1}; // col +const int D[3] = {0, 1, 2}; // depth + +struct ivec4; +struct ivec2; +struct usvec; +struct svec; + +struct ivec +{ + union + { + struct { int x, y, z; }; + struct { int r, g, b; }; + int v[3]; + }; + + ivec() {} + explicit ivec(const vec &v) : x(int(v.x)), y(int(v.y)), z(int(v.z)) {} + ivec(int a, int b, int c) : x(a), y(b), z(c) {} + ivec(int d, int row, int col, int depth) + { + v[R[d]] = row; + v[C[d]] = col; + v[D[d]] = depth; + } + ivec(int i, const ivec &co, int size) : x(co.x+((i&1)>>0)*size), y(co.y+((i&2)>>1)*size), z(co.z +((i&4)>>2)*size) {} + explicit ivec(const ivec4 &v); + explicit ivec(const ivec2 &v, int z = 0); + explicit ivec(const usvec &v); + explicit ivec(const svec &v); + + int &operator[](int i) { return v[i]; } + int operator[](int i) const { return v[i]; } + + //int idx(int i) { return v[i]; } + bool operator==(const ivec &v) const { return x==v.x && y==v.y && z==v.z; } + bool operator!=(const ivec &v) const { return x!=v.x || y!=v.y || z!=v.z; } + bool iszero() const { return x==0 && y==0 && z==0; } + ivec &shl(int n) { x<<= n; y<<= n; z<<= n; return *this; } + ivec &shr(int n) { x>>= n; y>>= n; z>>= n; return *this; } + ivec &mul(int n) { x *= n; y *= n; z *= n; return *this; } + ivec &div(int n) { x /= n; y /= n; z /= n; return *this; } + ivec &add(int n) { x += n; y += n; z += n; return *this; } + ivec &sub(int n) { x -= n; y -= n; z -= n; return *this; } + ivec &mul(const ivec &v) { x *= v.x; y *= v.y; z *= v.z; return *this; } + ivec &div(const ivec &v) { x /= v.x; y /= v.y; z /= v.z; return *this; } + ivec &add(const ivec &v) { x += v.x; y += v.y; z += v.z; return *this; } + ivec &sub(const ivec &v) { x -= v.x; y -= v.y; z -= v.z; return *this; } + ivec &mask(int n) { x &= n; y &= n; z &= n; return *this; } + ivec &neg() { return mul(-1); } + ivec &min(const ivec &o) { x = ::min(x, o.x); y = ::min(y, o.y); z = ::min(z, o.z); return *this; } + ivec &max(const ivec &o) { x = ::max(x, o.x); y = ::max(y, o.y); z = ::max(z, o.z); return *this; } + ivec &min(int n) { x = ::min(x, n); y = ::min(y, n); z = ::min(z, n); return *this; } + ivec &max(int n) { x = ::max(x, n); y = ::max(y, n); z = ::max(z, n); return *this; } + ivec &abs() { x = ::abs(x); y = ::abs(y); z = ::abs(z); return *this; } + ivec &clamp(int l, int h) { x = ::clamp(x, l, h); y = ::clamp(y, l, h); z = ::clamp(z, l, h); return *this; } + ivec &cross(const ivec &a, const ivec &b) { x = a.y*b.z-a.z*b.y; y = a.z*b.x-a.x*b.z; z = a.x*b.y-a.y*b.x; return *this; } + int dot(const ivec &o) const { return x*o.x + y*o.y + z*o.z; } + float dist(const plane &p) const { return x*p.x + y*p.y + z*p.z + p.offset; } + + static inline ivec floor(const vec &o) { return ivec(int(::floor(o.x)), int(::floor(o.y)), int(::floor(o.z))); } + static inline ivec ceil(const vec &o) { return ivec(int(::ceil(o.x)), int(::ceil(o.y)), int(::ceil(o.z))); } +}; + +inline vec::vec(const ivec &v) : x(v.x), y(v.y), z(v.z) {} + +static inline bool htcmp(const ivec &x, const ivec &y) +{ + return x == y; +} + +static inline uint hthash(const ivec &k) +{ + return k.x^k.y^k.z; +} + +struct ivec2 +{ + union + { + struct { int x, y; }; + int v[2]; + }; + + ivec2() {} + ivec2(int x, int y) : x(x), y(y) {} + explicit ivec2(const vec2 &v) : x(int(v.x)), y(int(v.y)) {} + explicit ivec2(const ivec &v) : x(v.x), y(v.y) {} + + int &operator[](int i) { return v[i]; } + int operator[](int i) const { return v[i]; } + + bool operator==(const ivec2 &o) const { return x == o.x && y == o.y; } + bool operator!=(const ivec2 &o) const { return x != o.x || y != o.y; } + + bool iszero() const { return x==0 && y==0; } + ivec2 &shl(int n) { x<<= n; y<<= n; return *this; } + ivec2 &shr(int n) { x>>= n; y>>= n; return *this; } + ivec2 &mul(int n) { x *= n; y *= n; return *this; } + ivec2 &div(int n) { x /= n; y /= n; return *this; } + ivec2 &add(int n) { x += n; y += n; return *this; } + ivec2 &sub(int n) { x -= n; y -= n; return *this; } + ivec2 &mul(const ivec2 &v) { x *= v.x; y *= v.y; return *this; } + ivec2 &div(const ivec2 &v) { x /= v.x; y /= v.y; return *this; } + ivec2 &add(const ivec2 &v) { x += v.x; y += v.y; return *this; } + ivec2 &sub(const ivec2 &v) { x -= v.x; y -= v.y; return *this; } + ivec2 &mask(int n) { x &= n; y &= n; return *this; } + ivec2 &neg() { x = -x; y = -y; return *this; } + ivec2 &min(const ivec2 &o) { x = ::min(x, o.x); y = ::min(y, o.y); return *this; } + ivec2 &max(const ivec2 &o) { x = ::max(x, o.x); y = ::max(y, o.y); return *this; } + ivec2 &min(int n) { x = ::min(x, n); y = ::min(y, n); return *this; } + ivec2 &max(int n) { x = ::max(x, n); y = ::max(y, n); return *this; } + ivec2 &abs() { x = ::abs(x); y = ::abs(y); return *this; } + int dot(const ivec2 &o) const { return x*o.x + y*o.y; } + int cross(const ivec2 &o) const { return x*o.y - y*o.x; } +}; + +inline ivec::ivec(const ivec2 &v, int z) : x(v.x), y(v.y), z(z) {} + +static inline bool htcmp(const ivec2 &x, const ivec2 &y) +{ + return x == y; +} + +static inline uint hthash(const ivec2 &k) +{ + return k.x^k.y; +} + +struct ivec4 +{ + union + { + struct { int x, y, z, w; }; + struct { int r, g, b, a; }; + int v[4]; + }; + + ivec4() {} + explicit ivec4(const ivec &p, int w = 0) : x(p.x), y(p.y), z(p.z), w(w) {} + ivec4(int x, int y, int z, int w) : x(x), y(y), z(z), w(w) {} + explicit ivec4(const vec4 &v) : x(int(v.x)), y(int(v.y)), z(int(v.z)), w(int(v.w)) {} + + bool operator==(const ivec4 &o) const { return x == o.x && y == o.y && z == o.z && w == o.w; } + bool operator!=(const ivec4 &o) const { return x != o.x || y != o.y || z != o.z || w != o.w; } +}; + +inline ivec::ivec(const ivec4 &v) : x(v.x), y(v.y), z(v.z) {} + +static inline bool htcmp(const ivec4 &x, const ivec4 &y) +{ + return x == y; +} + +static inline uint hthash(const ivec4 &k) +{ + return k.x^k.y^k.z^k.w; +} + +struct bvec4; + +struct bvec +{ + union + { + struct { uchar x, y, z; }; + struct { uchar r, g, b; }; + uchar v[3]; + }; + + bvec() {} + bvec(uchar x, uchar y, uchar z) : x(x), y(y), z(z) {} + explicit bvec(const vec &v) : x(uchar((v.x+1)*(255.0f/2.0f))), y(uchar((v.y+1)*(255.0f/2.0f))), z(uchar((v.z+1)*(255.0f/2.0f))) {} + explicit bvec(const bvec4 &v); + + uchar &operator[](int i) { return v[i]; } + uchar operator[](int i) const { return v[i]; } + + bool operator==(const bvec &v) const { return x==v.x && y==v.y && z==v.z; } + bool operator!=(const bvec &v) const { return x!=v.x || y!=v.y || z!=v.z; } + + bool iszero() const { return x==0 && y==0 && z==0; } + + vec tonormal() const { return vec(x*(2.0f/255.0f)-1.0f, y*(2.0f/255.0f)-1.0f, z*(2.0f/255.0f)-1.0f); } + + bvec &normalize() + { + vec n(x-127.5f, y-127.5f, z-127.5f); + float mag = 127.5f/n.magnitude(); + x = uchar(n.x*mag+127.5f); + y = uchar(n.y*mag+127.5f); + z = uchar(n.z*mag+127.5f); + return *this; + } + + void lerp(const bvec &a, const bvec &b, float t) { x = uchar(a.x + (b.x-a.x)*t); y = uchar(a.y + (b.y-a.y)*t); z = uchar(a.z + (b.z-a.z)*t); } + + void lerp(const bvec &a, const bvec &b, int ka, int kb, int d) + { + x = uchar((a.x*ka + b.x*kb)/d); + y = uchar((a.y*ka + b.y*kb)/d); + z = uchar((a.z*ka + b.z*kb)/d); + } + + void flip() { x ^= 0x80; y ^= 0x80; z ^= 0x80; } + + void scale(int k, int d) { x = uchar((x*k)/d); y = uchar((y*k)/d); z = uchar((z*k)/d); } + + bvec &shl(int n) { x<<= n; y<<= n; z<<= n; return *this; } + bvec &shr(int n) { x>>= n; y>>= n; z>>= n; return *this; } + + static bvec fromcolor(const vec &v) { return bvec(uchar(v.x*255.0f), uchar(v.y*255.0f), uchar(v.z*255.0f)); } + vec tocolor() const { return vec(x*(1.0f/255.0f), y*(1.0f/255.0f), z*(1.0f/255.0f)); } + + static bvec from565(ushort c) { return bvec((((c>>11)&0x1F)*527 + 15) >> 6, (((c>>5)&0x3F)*259 + 35) >> 6, ((c&0x1F)*527 + 15) >> 6); } + + static bvec hexcolor(int color) + { + return bvec((color>>16)&0xFF, (color>>8)&0xFF, color&0xFF); + } + int tohexcolor() const { return (int(r)<<16)|(int(g)<<8)|int(b); } +}; + +struct bvec4 +{ + union + { + struct { uchar x, y, z, w; }; + struct { uchar r, g, b, a; }; + uchar v[4]; + uint mask; + }; + + bvec4() {} + bvec4(uchar x, uchar y, uchar z, uchar w = 0) : x(x), y(y), z(z), w(w) {} + bvec4(const bvec &v, uchar w = 0) : x(v.x), y(v.y), z(v.z), w(w) {} + + uchar &operator[](int i) { return v[i]; } + uchar operator[](int i) const { return v[i]; } + + bool operator==(const bvec4 &v) const { return mask==v.mask; } + bool operator!=(const bvec4 &v) const { return mask!=v.mask; } + + bool iszero() const { return mask==0; } + + vec tonormal() const { return vec(x*(2.0f/255.0f)-1.0f, y*(2.0f/255.0f)-1.0f, z*(2.0f/255.0f)-1.0f); } + + void lerp(const bvec4 &a, const bvec4 &b, float t) + { + x = uchar(a.x + (b.x-a.x)*t); + y = uchar(a.y + (b.y-a.y)*t); + z = uchar(a.z + (b.z-a.z)*t); + w = a.w; + } + + void lerp(const bvec4 &a, const bvec4 &b, int ka, int kb, int d) + { + x = uchar((a.x*ka + b.x*kb)/d); + y = uchar((a.y*ka + b.y*kb)/d); + z = uchar((a.z*ka + b.z*kb)/d); + w = a.w; + } + + void flip() { mask ^= 0x80808080; } +}; + +inline bvec::bvec(const bvec4 &v) : x(v.x), y(v.y), z(v.z) {} + +struct usvec +{ + union + { + struct { ushort x, y, z; }; + ushort v[3]; + }; + + ushort &operator[](int i) { return v[i]; } + ushort operator[](int i) const { return v[i]; } +}; + +inline ivec::ivec(const usvec &v) : x(v.x), y(v.y), z(v.z) {} + +struct svec +{ + union + { + struct { short x, y, z; }; + short v[3]; + }; + + svec() {} + svec(short x, short y, short z) : x(x), y(y), z(z) {} + explicit svec(const ivec &v) : x(v.x), y(v.y), z(v.z) {} + + short &operator[](int i) { return v[i]; } + short operator[](int i) const { return v[i]; } +}; + +inline ivec::ivec(const svec &v) : x(v.x), y(v.y), z(v.z) {} + +struct svec2 +{ + union + { + struct { short x, y; }; + short v[2]; + }; + + svec2() {} + svec2(short x, short y) : x(x), y(y) {} + + short &operator[](int i) { return v[i]; } + short operator[](int i) const { return v[i]; } + + bool operator==(const svec2 &o) const { return x == o.x && y == o.y; } + bool operator!=(const svec2 &o) const { return x != o.x || y != o.y; } + + bool iszero() const { return x==0 && y==0; } +}; + +struct dvec4 +{ + double x, y, z, w; + + dvec4() {} + dvec4(double x, double y, double z, double w) : x(x), y(y), z(z), w(w) {} + dvec4(const vec4 &v) : x(v.x), y(v.y), z(v.z), w(v.w) {} + + template<class B> dvec4 &madd(const dvec4 &a, const B &b) { return add(dvec4(a).mul(b)); } + dvec4 &mul(double f) { x *= f; y *= f; z *= f; w *= f; return *this; } + dvec4 &mul(const dvec4 &o) { x *= o.x; y *= o.y; z *= o.z; w *= o.w; return *this; } + dvec4 &add(double f) { x += f; y += f; z += f; w += f; return *this; } + dvec4 &add(const dvec4 &o) { x += o.x; y += o.y; z += o.z; w += o.w; return *this; } + + operator vec4() const { return vec4(x, y, z, w); } +}; + +struct matrix4 +{ + vec4 a, b, c, d; + + matrix4() {} + matrix4(const float *m) : a(m), b(m+4), c(m+8), d(m+12) {} + matrix4(const vec &a, const vec &b, const vec &c = vec(0, 0, 1)) + : a(a.x, b.x, c.x, 0), b(a.y, b.y, c.y, 0), c(a.z, b.z, c.z, 0), d(0, 0, 0, 1) + {} + matrix4(const vec4 &a, const vec4 &b, const vec4 &c, const vec4 &d = vec4(0, 0, 0, 1)) + : a(a), b(b), c(c), d(d) + {} + matrix4(const matrix4x3 &m) + : a(m.a, 0), b(m.b, 0), c(m.c, 0), d(m.d, 1) + {} + matrix4(const matrix3 &rot, const vec &trans) + : a(rot.a, 0), b(rot.b, 0), c(rot.c, 0), d(trans, 1) + {} + + void mul(const matrix4 &x, const matrix3 &y) + { + a = vec4(x.a).mul(y.a.x).madd(x.b, y.a.y).madd(x.c, y.a.z); + b = vec4(x.a).mul(y.b.x).madd(x.b, y.b.y).madd(x.c, y.b.z); + c = vec4(x.a).mul(y.c.x).madd(x.b, y.c.y).madd(x.c, y.c.z); + d = x.d; + } + void mul(const matrix3 &y) { mul(matrix4(*this), y); } + + template<class T> void mult(const matrix4 &x, const matrix4 &y) + { + a = T(x.a).mul(y.a.x).madd(x.b, y.a.y).madd(x.c, y.a.z).madd(x.d, y.a.w); + b = T(x.a).mul(y.b.x).madd(x.b, y.b.y).madd(x.c, y.b.z).madd(x.d, y.b.w); + c = T(x.a).mul(y.c.x).madd(x.b, y.c.y).madd(x.c, y.c.z).madd(x.d, y.c.w); + d = T(x.a).mul(y.d.x).madd(x.b, y.d.y).madd(x.c, y.d.z).madd(x.d, y.d.w); + } + + void mul(const matrix4 &x, const matrix4 &y) { mult<vec4>(x, y); } + void mul(const matrix4 &y) { mult<vec4>(matrix4(*this), y); } + + void muld(const matrix4 &x, const matrix4 &y) { mult<dvec4>(x, y); } + void muld(const matrix4 &y) { mult<dvec4>(matrix4(*this), y); } + + void rotate_around_x(float ck, float sk) + { + vec4 rb = vec4(b).mul(ck).madd(c, sk), + rc = vec4(c).mul(ck).msub(b, sk); + b = rb; + c = rc; + } + void rotate_around_x(float angle) { rotate_around_x(cosf(angle), sinf(angle)); } + void rotate_around_x(const vec2 &sc) { rotate_around_x(sc.x, sc.y); } + + void rotate_around_y(float ck, float sk) + { + vec4 rc = vec4(c).mul(ck).madd(a, sk), + ra = vec4(a).mul(ck).msub(c, sk); + c = rc; + a = ra; + } + void rotate_around_y(float angle) { rotate_around_y(cosf(angle), sinf(angle)); } + void rotate_around_y(const vec2 &sc) { rotate_around_y(sc.x, sc.y); } + + void rotate_around_z(float ck, float sk) + { + vec4 ra = vec4(a).mul(ck).madd(b, sk), + rb = vec4(b).mul(ck).msub(a, sk); + a = ra; + b = rb; + } + void rotate_around_z(float angle) { rotate_around_z(cosf(angle), sinf(angle)); } + void rotate_around_z(const vec2 &sc) { rotate_around_z(sc.x, sc.y); } + + void rotate(float ck, float sk, const vec &axis) + { + matrix3 m; + m.rotate(ck, sk, axis); + mul(m); + } + void rotate(float angle, const vec &dir) { rotate(cosf(angle), sinf(angle), dir); } + void rotate(const vec2 &sc, const vec &dir) { rotate(sc.x, sc.y, dir); } + + void identity() + { + a = vec4(1, 0, 0, 0); + b = vec4(0, 1, 0, 0); + c = vec4(0, 0, 1, 0); + d = vec4(0, 0, 0, 1); + } + + void settranslation(const vec &v) { d.setxyz(v); } + void settranslation(float x, float y, float z) { d.x = x; d.y = y; d.z = z; } + + void translate(const vec &p) { d.madd(a, p.x).madd(b, p.y).madd(c, p.z); } + void translate(float x, float y, float z) { translate(vec(x, y, z)); } + void translate(const vec &p, float scale) { translate(vec(p).mul(scale)); } + + void setscale(float x, float y, float z) { a.x = x; b.y = y; c.z = z; } + void setscale(const vec &v) { setscale(v.x, v.y, v.z); } + void setscale(float n) { setscale(n, n, n); } + + void scale(float x, float y, float z) + { + a.mul(x); + b.mul(y); + c.mul(z); + } + void scale(const vec &v) { scale(v.x, v.y, v.z); } + void scale(float n) { scale(n, n, n); } + + void scalexy(float x, float y) + { + a.x *= x; a.y *= y; + b.x *= x; b.y *= y; + c.x *= x; c.y *= y; + d.x *= x; d.y *= y; + } + + void scalez(float k) + { + a.z *= k; + b.z *= k; + c.z *= k; + d.z *= k; + } + + void reflectz(float z) + { + d.add(vec4(c).mul(2*z)); + c.neg(); + } + + void projective(float zscale = 0.5f, float zoffset = 0.5f) + { + a.x = 0.5f*(a.x + a.w); + a.y = 0.5f*(a.y + a.w); + b.x = 0.5f*(b.x + b.w); + b.y = 0.5f*(b.y + b.w); + c.x = 0.5f*(c.x + c.w); + c.y = 0.5f*(c.y + c.w); + d.x = 0.5f*(d.x + d.w); + d.y = 0.5f*(d.y + d.w); + a.z = zscale*a.z + zoffset*a.w; + b.z = zscale*b.z + zoffset*b.w; + c.z = zscale*c.z + zoffset*c.w; + d.z = zscale*d.z + zoffset*d.w; + } + + void jitter(float x, float y) + { + a.x += x * a.w; + a.y += y * a.w; + b.x += x * b.w; + b.y += y * b.w; + c.x += x * c.w; + c.y += y * c.w; + d.x += x * d.w; + d.y += y * d.w; + } + + void transpose() + { + swap(a.y, b.x); swap(a.z, c.x); swap(a.w, d.x); + swap(b.z, c.y); swap(b.w, d.y); + swap(c.w, d.z); + } + + void transpose(const matrix4 &m) + { + a = vec4(m.a.x, m.b.x, m.c.x, m.d.x); + b = vec4(m.a.y, m.b.y, m.c.y, m.d.y); + c = vec4(m.a.z, m.b.z, m.c.z, m.d.z); + d = vec4(m.a.w, m.b.w, m.c.w, m.d.w); + } + + void frustum(float left, float right, float bottom, float top, float znear, float zfar) + { + float width = right - left, height = top - bottom, zrange = znear - zfar; + a = vec4(2*znear/width, 0, 0, 0); + b = vec4(0, 2*znear/height, 0, 0); + c = vec4((right + left)/width, (top + bottom)/height, (zfar + znear)/zrange, -1); + d = vec4(0, 0, 2*znear*zfar/zrange, 0); + } + + void perspective(float fovy, float aspect, float znear, float zfar) + { + float ydist = znear * tan(fovy/2*RAD), xdist = ydist * aspect; + frustum(-xdist, xdist, -ydist, ydist, znear, zfar); + } + + void ortho(float left, float right, float bottom, float top, float znear, float zfar) + { + float width = right - left, height = top - bottom, zrange = znear - zfar; + a = vec4(2/width, 0, 0, 0); + b = vec4(0, 2/height, 0, 0); + c = vec4(0, 0, 2/zrange, 0); + d = vec4(-(right+left)/width, -(top+bottom)/height, (zfar+znear)/zrange, 1); + } + + void clip(const plane &p, const matrix4 &m) + { + float x = ((p.x<0 ? -1 : (p.x>0 ? 1 : 0)) + m.c.x) / m.a.x, + y = ((p.y<0 ? -1 : (p.y>0 ? 1 : 0)) + m.c.y) / m.b.y, + w = (1 + m.c.z) / m.d.z, + scale = 2 / (x*p.x + y*p.y - p.z + w*p.offset); + a = vec4(m.a.x, m.a.y, p.x*scale, m.a.w); + b = vec4(m.b.x, m.b.y, p.y*scale, m.b.w); + c = vec4(m.c.x, m.c.y, p.z*scale + 1.0f, m.c.w); + d = vec4(m.d.x, m.d.y, p.offset*scale, m.d.w); + } + + void transform(const vec &in, vec &out) const + { + out = vec(a).mul(in.x).add(vec(b).mul(in.y)).add(vec(c).mul(in.z)).add(vec(d)); + } + + void transform(const vec4 &in, vec &out) const + { + out = vec(a).mul(in.x).add(vec(b).mul(in.y)).add(vec(c).mul(in.z)).add(vec(d).mul(in.w)); + } + + void transform(const vec &in, vec4 &out) const + { + out = vec4(a).mul(in.x).madd(b, in.y).madd(c, in.z).add(d); + } + + void transform(const vec4 &in, vec4 &out) const + { + out = vec4(a).mul(in.x).madd(b, in.y).madd(c, in.z).madd(d, in.w); + } + + template<class T, class U> T transform(const U &in) const + { + T v; + transform(in, v); + return v; + } + + template<class T> vec perspectivetransform(const T &in) const + { + vec4 v; + transform(in, v); + return vec(v).div(v.w); + } + + void transformnormal(const vec &in, vec &out) const + { + out = vec(a).mul(in.x).add(vec(b).mul(in.y)).add(vec(c).mul(in.z)); + } + + void transformnormal(const vec &in, vec4 &out) const + { + out = vec4(a).mul(in.x).madd(b, in.y).madd(c, in.z); + } + + template<class T, class U> T transformnormal(const U &in) const + { + T v; + transformnormal(in, v); + return v; + } + + void transposedtransform(const vec &in, vec &out) const + { + vec p = vec(in).sub(vec(d)); + out.x = a.dot3(p); + out.y = b.dot3(p); + out.z = c.dot3(p); + } + + void transposedtransformnormal(const vec &in, vec &out) const + { + out.x = a.dot3(in); + out.y = b.dot3(in); + out.z = c.dot3(in); + } + + void transposedtransform(const plane &in, plane &out) const + { + out.x = in.dist(a); + out.y = in.dist(b); + out.z = in.dist(c); + out.offset = in.dist(d); + } + + float getscale() const + { + return sqrtf(a.x*a.y + b.x*b.x + c.x*c.x); + } + + vec gettranslation() const + { + return vec(d); + } + + vec4 rowx() const { return vec4(a.x, b.x, c.x, d.x); } + vec4 rowy() const { return vec4(a.y, b.y, c.y, d.y); } + vec4 rowz() const { return vec4(a.z, b.z, c.z, d.z); } + vec4 roww() const { return vec4(a.w, b.w, c.w, d.w); } + + bool invert(const matrix4 &m, double mindet = 1.0e-12); + + vec2 lineardepthscale() const + { + return vec2(d.w, -d.z).div(c.z*d.w - d.z*c.w); + } +}; + +inline matrix3::matrix3(const matrix4 &m) + : a(m.a), b(m.b), c(m.c) +{} + +inline matrix4x3::matrix4x3(const matrix4 &m) + : a(m.a), b(m.b), c(m.c), d(m.d) +{} + +struct matrix2 +{ + vec2 a, b; + + matrix2() {} + matrix2(const vec2 &a, const vec2 &b) : a(a), b(b) {} + explicit matrix2(const matrix4 &m) : a(m.a), b(m.b) {} + explicit matrix2(const matrix3 &m) : a(m.a), b(m.b) {} +}; + +struct squat +{ + short x, y, z, w; + + squat() {} + squat(const vec4 &q) { convert(q); } + + void convert(const vec4 &q) + { + x = short(q.x*32767.5f-0.5f); + y = short(q.y*32767.5f-0.5f); + z = short(q.z*32767.5f-0.5f); + w = short(q.w*32767.5f-0.5f); + } + + void lerp(const vec4 &a, const vec4 &b, float t) + { + vec4 q; + q.lerp(a, b, t); + convert(q); + } +}; + +extern bool raysphereintersect(const vec ¢er, float radius, const vec &o, const vec &ray, float &dist); +extern bool rayboxintersect(const vec &b, const vec &s, const vec &o, const vec &ray, float &dist, int &orient); +extern bool linecylinderintersect(const vec &from, const vec &to, const vec &start, const vec &end, float radius, float &dist); + +extern const vec2 sincos360[]; +static inline int mod360(int angle) +{ + if(angle < 0) angle = 360 + (angle <= -360 ? angle%360 : angle); + else if(angle >= 360) angle %= 360; + return angle; +} +static inline const vec2 &sincosmod360(int angle) { return sincos360[mod360(angle)]; } +static inline float cos360(int angle) { return sincos360[angle].x; } +static inline float sin360(int angle) { return sincos360[angle].y; } +static inline float tan360(int angle) { const vec2 &sc = sincos360[angle]; return sc.y/sc.x; } +static inline float cotan360(int angle) { const vec2 &sc = sincos360[angle]; return sc.x/sc.y; } + diff --git a/src/shared/glemu.cpp b/src/shared/glemu.cpp new file mode 100644 index 0000000..5658eb8 --- /dev/null +++ b/src/shared/glemu.cpp @@ -0,0 +1,355 @@ +#include "cube.h" + +extern int glversion; +extern int intel_mapbufferrange_bug; + +namespace gle +{ + struct attribinfo + { + int type, size, formatsize, offset; + GLenum format; + + attribinfo() : type(0), size(0), formatsize(0), offset(0), format(GL_FALSE) {} + + bool operator==(const attribinfo &a) const + { + return type == a.type && size == a.size && format == a.format && offset == a.offset; + } + bool operator!=(const attribinfo &a) const + { + return type != a.type || size != a.size || format != a.format || offset != a.offset; + } + }; + + extern const char * const attribnames[MAXATTRIBS] = { "vvertex", "vcolor", "vtexcoord0", "vtexcoord1", "vnormal", "vtangent", "vboneweight", "vboneindex" }; + ucharbuf attribbuf; + static uchar *attribdata; + static attribinfo attribdefs[MAXATTRIBS], lastattribs[MAXATTRIBS]; + int enabled = 0; + static int numattribs = 0, attribmask = 0, numlastattribs = 0, lastattribmask = 0, vertexsize = 0, lastvertexsize = 0; + static GLenum primtype = GL_TRIANGLES; + static uchar *lastbuf = NULL; + static bool changedattribs = false; + static vector<GLint> multidrawstart; + static vector<GLsizei> multidrawcount; + + #define MAXQUADS (0x10000/4) + static GLuint quadindexes = 0; + static bool quadsenabled = false; + + #define MAXVBOSIZE (4*1024*1024) + static GLuint vbo = 0; + static int vbooffset = MAXVBOSIZE; + + static GLuint defaultvao = 0; + + void enablequads() + { + quadsenabled = true; + + if(glversion < 300) return; + + if(quadindexes) + { + glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER, quadindexes); + return; + } + + glGenBuffers_(1, &quadindexes); + ushort *data = new ushort[MAXQUADS*6], *dst = data; + for(int idx = 0; idx < MAXQUADS*4; idx += 4, dst += 6) + { + dst[0] = idx; + dst[1] = idx + 1; + dst[2] = idx + 2; + dst[3] = idx + 0; + dst[4] = idx + 2; + dst[5] = idx + 3; + } + glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER, quadindexes); + glBufferData_(GL_ELEMENT_ARRAY_BUFFER, MAXQUADS*6*sizeof(ushort), data, GL_STATIC_DRAW); + delete[] data; + } + + void disablequads() + { + quadsenabled = false; + + if(glversion < 300) return; + + glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER, 0); + } + + void drawquads(int offset, int count) + { + if(count <= 0) return; + if(glversion < 300) + { + glDrawArrays(GL_QUADS, offset*4, count*4); + return; + } + if(offset + count > MAXQUADS) + { + if(offset >= MAXQUADS) return; + count = MAXQUADS - offset; + } + glDrawRangeElements_(GL_TRIANGLES, offset*4, (offset + count)*4-1, count*6, GL_UNSIGNED_SHORT, (ushort *)0 + offset*6); + } + + void defattrib(int type, int size, int format) + { + if(type == ATTRIB_VERTEX) + { + numattribs = attribmask = 0; + vertexsize = 0; + } + changedattribs = true; + attribmask |= 1<<type; + attribinfo &a = attribdefs[numattribs++]; + a.type = type; + a.size = size; + a.format = format; + switch(format) + { + case 'B': case GL_UNSIGNED_BYTE: a.formatsize = 1; a.format = GL_UNSIGNED_BYTE; break; + case 'b': case GL_BYTE: a.formatsize = 1; a.format = GL_BYTE; break; + case 'S': case GL_UNSIGNED_SHORT: a.formatsize = 2; a.format = GL_UNSIGNED_SHORT; break; + case 's': case GL_SHORT: a.formatsize = 2; a.format = GL_SHORT; break; + case 'I': case GL_UNSIGNED_INT: a.formatsize = 4; a.format = GL_UNSIGNED_INT; break; + case 'i': case GL_INT: a.formatsize = 4; a.format = GL_INT; break; + case 'f': case GL_FLOAT: a.formatsize = 4; a.format = GL_FLOAT; break; + case 'd': case GL_DOUBLE: a.formatsize = 8; a.format = GL_DOUBLE; break; + default: a.formatsize = 0; a.format = GL_FALSE; break; + } + a.formatsize *= size; + a.offset = vertexsize; + vertexsize += a.formatsize; + } + + void defattribs(const char *fmt) + { + for(;; fmt += 3) + { + GLenum format; + switch(fmt[0]) + { + case 'v': format = ATTRIB_VERTEX; break; + case 'c': format = ATTRIB_COLOR; break; + case 't': format = ATTRIB_TEXCOORD0; break; + case 'T': format = ATTRIB_TEXCOORD1; break; + case 'n': format = ATTRIB_NORMAL; break; + case 'x': format = ATTRIB_TANGENT; break; + case 'w': format = ATTRIB_BONEWEIGHT; break; + case 'i': format = ATTRIB_BONEINDEX; break; + default: return; + } + defattrib(format, fmt[1]-'0', fmt[2]); + } + } + + static inline void setattrib(const attribinfo &a, uchar *buf) + { + switch(a.type) + { + case ATTRIB_VERTEX: + case ATTRIB_TEXCOORD0: + case ATTRIB_TEXCOORD1: + case ATTRIB_BONEINDEX: + glVertexAttribPointer_(a.type, a.size, a.format, GL_FALSE, vertexsize, buf); + break; + case ATTRIB_COLOR: + case ATTRIB_NORMAL: + case ATTRIB_TANGENT: + case ATTRIB_BONEWEIGHT: + glVertexAttribPointer_(a.type, a.size, a.format, GL_TRUE, vertexsize, buf); + break; + } + if(!(enabled&(1<<a.type))) + { + glEnableVertexAttribArray_(a.type); + enabled |= 1<<a.type; + } + } + + static inline void unsetattrib(const attribinfo &a) + { + glDisableVertexAttribArray_(a.type); + enabled &= ~(1<<a.type); + } + + static inline void setattribs(uchar *buf) + { + bool forceattribs = numattribs != numlastattribs || vertexsize != lastvertexsize || buf != lastbuf; + if(forceattribs || changedattribs) + { + int diffmask = enabled & lastattribmask & ~attribmask; + if(diffmask) loopi(numlastattribs) + { + const attribinfo &a = lastattribs[i]; + if(diffmask & (1<<a.type)) unsetattrib(a); + } + uchar *src = buf; + loopi(numattribs) + { + const attribinfo &a = attribdefs[i]; + if(forceattribs || a != lastattribs[i]) + { + setattrib(a, src); + lastattribs[i] = a; + } + src += a.formatsize; + } + lastbuf = buf; + numlastattribs = numattribs; + lastattribmask = attribmask; + lastvertexsize = vertexsize; + changedattribs = false; + } + } + + void begin(GLenum mode) + { + primtype = mode; + } + + void begin(GLenum mode, int numverts) + { + primtype = mode; + if(glversion >= 300 && !intel_mapbufferrange_bug) + { + int len = numverts * vertexsize; + if(vbooffset + len >= MAXVBOSIZE) + { + len = min(len, MAXVBOSIZE); + if(!vbo) glGenBuffers_(1, &vbo); + glBindBuffer_(GL_ARRAY_BUFFER, vbo); + glBufferData_(GL_ARRAY_BUFFER, MAXVBOSIZE, NULL, GL_STREAM_DRAW); + vbooffset = 0; + } + else if(!lastvertexsize) glBindBuffer_(GL_ARRAY_BUFFER, vbo); + void *buf = glMapBufferRange_(GL_ARRAY_BUFFER, vbooffset, len, GL_MAP_WRITE_BIT|GL_MAP_INVALIDATE_RANGE_BIT|GL_MAP_UNSYNCHRONIZED_BIT); + if(buf) attribbuf.reset((uchar *)buf, len); + } + } + + void multidraw() + { + int start = multidrawstart.length() ? multidrawstart.last() + multidrawcount.last() : 0, + count = attribbuf.length()/vertexsize - start; + if(count > 0) + { + multidrawstart.add(start); + multidrawcount.add(count); + } + } + + int end() + { + uchar *buf = attribbuf.getbuf(); + if(attribbuf.empty()) + { + if(buf != attribdata) + { + glUnmapBuffer_(GL_ARRAY_BUFFER); + attribbuf.reset(attribdata, MAXVBOSIZE); + } + return 0; + } + int start = 0; + if(glversion >= 300) + { + if(buf == attribdata) + { + if(vbooffset + attribbuf.length() >= MAXVBOSIZE) + { + if(!vbo) glGenBuffers_(1, &vbo); + glBindBuffer_(GL_ARRAY_BUFFER, vbo); + glBufferData_(GL_ARRAY_BUFFER, MAXVBOSIZE, NULL, GL_STREAM_DRAW); + vbooffset = 0; + } + else if(!lastvertexsize) glBindBuffer_(GL_ARRAY_BUFFER, vbo); + void *dst = intel_mapbufferrange_bug ? NULL : + glMapBufferRange_(GL_ARRAY_BUFFER, vbooffset, attribbuf.length(), GL_MAP_WRITE_BIT|GL_MAP_INVALIDATE_RANGE_BIT|GL_MAP_UNSYNCHRONIZED_BIT); + if(dst) + { + memcpy(dst, attribbuf.getbuf(), attribbuf.length()); + glUnmapBuffer_(GL_ARRAY_BUFFER); + } + else glBufferSubData_(GL_ARRAY_BUFFER, vbooffset, attribbuf.length(), attribbuf.getbuf()); + } + else glUnmapBuffer_(GL_ARRAY_BUFFER); + buf = (uchar *)0 + vbooffset; + if(vertexsize == lastvertexsize && buf >= lastbuf) + { + start = int(buf - lastbuf)/vertexsize; + if(primtype == GL_QUADS && (start%4 || start + attribbuf.length()/vertexsize >= 4*MAXQUADS)) + start = 0; + else buf = lastbuf; + } + vbooffset += attribbuf.length(); + } + setattribs(buf); + int numvertexes = attribbuf.length()/vertexsize; + if(primtype == GL_QUADS) + { + if(!quadsenabled) enablequads(); + for(int quads = numvertexes/4;;) + { + int count = min(quads, MAXQUADS); + drawquads(start/4, count); + quads -= count; + if(quads <= 0) break; + setattribs(buf + 4*count*vertexsize); + start = 0; + } + } + else + { + if(multidrawstart.length()) + { + multidraw(); + if(start) loopv(multidrawstart) multidrawstart[i] += start; + glMultiDrawArrays_(primtype, multidrawstart.getbuf(), multidrawcount.getbuf(), multidrawstart.length()); + multidrawstart.setsize(0); + multidrawcount.setsize(0); + } + else glDrawArrays(primtype, start, numvertexes); + } + attribbuf.reset(attribdata, MAXVBOSIZE); + return numvertexes; + } + + void forcedisable() + { + for(int i = 0; enabled; i++) if(enabled&(1<<i)) { glDisableVertexAttribArray_(i); enabled &= ~(1<<i); } + numlastattribs = lastattribmask = lastvertexsize = 0; + lastbuf = NULL; + if(quadsenabled) disablequads(); + if(glversion >= 300) glBindBuffer_(GL_ARRAY_BUFFER, 0); + } + + void setup() + { + if(glversion >= 300) + { + if(!defaultvao) glGenVertexArrays_(1, &defaultvao); + glBindVertexArray_(defaultvao); + } + attribdata = new uchar[MAXVBOSIZE]; + attribbuf.reset(attribdata, MAXVBOSIZE); + } + + void cleanup() + { + disable(); + + if(quadindexes) { glDeleteBuffers_(1, &quadindexes); quadindexes = 0; } + + if(vbo) { glDeleteBuffers_(1, &vbo); vbo = 0; } + vbooffset = MAXVBOSIZE; + + if(defaultvao) { glDeleteVertexArrays_(1, &defaultvao); defaultvao = 0; } + } +} + diff --git a/src/shared/glemu.h b/src/shared/glemu.h new file mode 100644 index 0000000..94af8f9 --- /dev/null +++ b/src/shared/glemu.h @@ -0,0 +1,180 @@ +namespace gle +{ + enum + { + ATTRIB_VERTEX = 0, + ATTRIB_COLOR = 1, + ATTRIB_TEXCOORD0 = 2, + ATTRIB_TEXCOORD1 = 3, + ATTRIB_NORMAL = 4, + ATTRIB_TANGENT = 5, + ATTRIB_BONEWEIGHT = 6, + ATTRIB_BONEINDEX = 7, + MAXATTRIBS = 8 + }; + + extern const char * const attribnames[MAXATTRIBS]; + extern ucharbuf attribbuf; + + extern int enabled; + extern void forcedisable(); + static inline void disable() { if(enabled) forcedisable(); } + + extern void begin(GLenum mode); + extern void begin(GLenum mode, int numverts); + extern void defattribs(const char *fmt); + extern void defattrib(int type, int size, int format); + + #define GLE_DEFATTRIB(name, type, defaultsize, defaultformat) \ + static inline void def##name(int size = defaultsize, int format = defaultformat) { defattrib(type, size, format); } + + GLE_DEFATTRIB(vertex, ATTRIB_VERTEX, 3, GL_FLOAT) + GLE_DEFATTRIB(color, ATTRIB_COLOR, 3, GL_FLOAT) + GLE_DEFATTRIB(texcoord0, ATTRIB_TEXCOORD0, 2, GL_FLOAT) + GLE_DEFATTRIB(texcoord1, ATTRIB_TEXCOORD1, 2, GL_FLOAT) + GLE_DEFATTRIB(normal, ATTRIB_NORMAL, 3, GL_FLOAT) + GLE_DEFATTRIB(tangent, ATTRIB_TANGENT, 4, GL_FLOAT) + GLE_DEFATTRIB(boneweight, ATTRIB_BONEWEIGHT, 4, GL_UNSIGNED_BYTE) + GLE_DEFATTRIB(boneindex, ATTRIB_BONEINDEX, 4, GL_UNSIGNED_BYTE) + + #define GLE_INITATTRIB(name, index, suffix, type) \ + static inline void name##suffix(type x) { glVertexAttrib1##suffix##_(index, x); } \ + static inline void name##suffix(type x, type y) { glVertexAttrib2##suffix##_(index, x, y); } \ + static inline void name##suffix(type x, type y, type z) { glVertexAttrib3##suffix##_(index, x, y, z); } \ + static inline void name##suffix(type x, type y, type z, type w) { glVertexAttrib4##suffix##_(index, x, y, z, w); } + #define GLE_INITATTRIBF(name, index) \ + GLE_INITATTRIB(name, index, f, float) \ + static inline void name(const vec &v) { glVertexAttrib3fv_(index, v.v); } \ + static inline void name(const vec &v, float w) { glVertexAttrib4f_(index, v.x, v.y, v.z, w); } \ + static inline void name(const vec2 &v) { glVertexAttrib2fv_(index, v.v); } \ + static inline void name(const vec4 &v) { glVertexAttrib4fv_(index, v.v); } + #define GLE_INITATTRIBN(name, index, suffix, type, defaultw) \ + static inline void name##suffix(type x, type y, type z, type w = defaultw) { glVertexAttrib4N##suffix##_(index, x, y, z, w); } + + GLE_INITATTRIBF(vertex, ATTRIB_VERTEX) + GLE_INITATTRIBF(color, ATTRIB_COLOR) + static inline void color(const bvec4 &v) { glVertexAttrib4Nubv_(ATTRIB_COLOR, v.v); } + static inline void color(const bvec &v, uchar alpha = 255) { color(bvec4(v, alpha)); } + static inline void colorub(uchar x, uchar y, uchar z, uchar w = 255) { color(bvec4(x, y, z, w)); } + GLE_INITATTRIBF(texcoord0, ATTRIB_TEXCOORD0) + GLE_INITATTRIBF(texcoord1, ATTRIB_TEXCOORD1) + static inline void normal(float x, float y, float z) { glVertexAttrib4f_(ATTRIB_NORMAL, x, y, z, 0.0f); } + static inline void normal(const vec &v) { glVertexAttrib4f_(ATTRIB_NORMAL, v.x, v.y, v.z, 0.0f); } + static inline void tangent(float x, float y, float z, float w = 1.0f) { glVertexAttrib4f_(ATTRIB_TANGENT, x, y, z, w); } + static inline void tangent(const vec &v, float w = 1.0f) { glVertexAttrib4f_(ATTRIB_TANGENT, v.x, v.y, v.z, w); } + static inline void tangent(const vec4 &v) { glVertexAttrib4fv_(ATTRIB_TANGENT, v.v); } + + #define GLE_ATTRIBPOINTER(name, index, defaultnormalized, defaultsize, defaulttype, prepare) \ + static inline void enable##name() { prepare; glEnableVertexAttribArray_(index); } \ + static inline void disable##name() { glDisableVertexAttribArray_(index); } \ + static inline void name##pointer(int stride, const void *data, GLenum type = defaulttype, int size = defaultsize, GLenum normalized = defaultnormalized) { \ + prepare; \ + glVertexAttribPointer_(index, size, type, normalized, stride, data); \ + } + + static inline void enableattrib(int index) { disable(); glEnableVertexAttribArray_(index); } + static inline void disableattrib(int index) { glDisableVertexAttribArray_(index); } + GLE_ATTRIBPOINTER(vertex, ATTRIB_VERTEX, GL_FALSE, 3, GL_FLOAT, disable()) + GLE_ATTRIBPOINTER(color, ATTRIB_COLOR, GL_TRUE, 4, GL_UNSIGNED_BYTE, ) + GLE_ATTRIBPOINTER(texcoord0, ATTRIB_TEXCOORD0, GL_FALSE, 2, GL_FLOAT, ) + GLE_ATTRIBPOINTER(texcoord1, ATTRIB_TEXCOORD1, GL_FALSE, 2, GL_FLOAT, ) + GLE_ATTRIBPOINTER(normal, ATTRIB_NORMAL, GL_TRUE, 3, GL_FLOAT, ) + GLE_ATTRIBPOINTER(tangent, ATTRIB_TANGENT, GL_TRUE, 4, GL_FLOAT, ) + GLE_ATTRIBPOINTER(boneweight, ATTRIB_BONEWEIGHT, GL_TRUE, 4, GL_UNSIGNED_BYTE, ) + GLE_ATTRIBPOINTER(boneindex, ATTRIB_BONEINDEX, GL_FALSE, 4, GL_UNSIGNED_BYTE, ) + + static inline void bindebo(GLuint ebo) { disable(); glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER, ebo); } + static inline void clearebo() { glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER, 0); } + static inline void bindvbo(GLuint vbo) { disable(); glBindBuffer_(GL_ARRAY_BUFFER, vbo); } + static inline void clearvbo() { glBindBuffer_(GL_ARRAY_BUFFER, 0); } + + template<class T> + static inline void attrib(T x) + { + if(attribbuf.check(sizeof(T))) + { + T *buf = (T *)attribbuf.pad(sizeof(T)); + buf[0] = x; + } + } + + template<class T> + static inline void attrib(T x, T y) + { + if(attribbuf.check(2*sizeof(T))) + { + T *buf = (T *)attribbuf.pad(2*sizeof(T)); + buf[0] = x; + buf[1] = y; + } + } + + template<class T> + static inline void attrib(T x, T y, T z) + { + if(attribbuf.check(3*sizeof(T))) + { + T *buf = (T *)attribbuf.pad(3*sizeof(T)); + buf[0] = x; + buf[1] = y; + buf[2] = z; + } + } + + template<class T> + static inline void attrib(T x, T y, T z, T w) + { + if(attribbuf.check(4*sizeof(T))) + { + T *buf = (T *)attribbuf.pad(4*sizeof(T)); + buf[0] = x; + buf[1] = y; + buf[2] = z; + buf[3] = w; + } + } + + template<size_t N, class T> + static inline void attribv(const T *v) + { + attribbuf.put((const uchar *)v, N*sizeof(T)); + } + + #define GLE_ATTRIB(suffix, type) \ + static inline void attrib##suffix(type x) { attrib<type>(x); } \ + static inline void attrib##suffix(type x, type y) { attrib<type>(x, y); } \ + static inline void attrib##suffix(type x, type y, type z) { attrib<type>(x, y, z); } \ + static inline void attrib##suffix(type x, type y, type z, type w) { attrib<type>(x, y, z, w); } + + GLE_ATTRIB(f, float) + GLE_ATTRIB(d, double) + GLE_ATTRIB(b, char) + GLE_ATTRIB(ub, uchar) + GLE_ATTRIB(s, short) + GLE_ATTRIB(us, ushort) + GLE_ATTRIB(i, int) + GLE_ATTRIB(ui, uint) + + static inline void attrib(const vec &v) { attribf(v.x, v.y, v.z); } + static inline void attrib(const vec &v, float w) { attribf(v.x, v.y, v.z, w); } + static inline void attrib(const vec2 &v) { attribf(v.x, v.y); } + static inline void attrib(const vec4 &v) { attribf(v.x, v.y, v.z, v.w); } + static inline void attrib(const ivec &v) { attribi(v.x, v.y, v.z); } + static inline void attrib(const ivec &v, int w) { attribi(v.x, v.y, v.z, w); } + static inline void attrib(const ivec2 &v) { attribi(v.x, v.y); } + static inline void attrib(const ivec4 &v) { attribi(v.x, v.y, v.z, v.w); } + static inline void attrib(const bvec &b) { attribub(b.x, b.y, b.z); } + static inline void attrib(const bvec &b, uchar w) { attribub(b.x, b.y, b.z, w); } + static inline void attrib(const bvec4 &b) { attribub(b.x, b.y, b.z, b.w); } + + extern void multidraw(); + extern int end(); + + extern void enablequads(); + extern void disablequads(); + extern void drawquads(int offset, int count); + + extern void setup(); + extern void cleanup(); +} + diff --git a/src/shared/glexts.h b/src/shared/glexts.h new file mode 100644 index 0000000..59509c1 --- /dev/null +++ b/src/shared/glexts.h @@ -0,0 +1,488 @@ +#ifndef APIENTRY +#define APIENTRY +#endif +#ifndef APIENTRYP +#define APIENTRYP APIENTRY * +#endif + +// OpenGL deprecated functionality +#ifndef GL_QUADS +#define GL_QUADS 0x0007 +#endif + +#ifndef GL_ALPHA +#define GL_ALPHA 0x1906 +#endif +#ifndef GL_ALPHA8 +#define GL_ALPHA8 0x803C +#endif +#ifndef GL_ALPHA16 +#define GL_ALPHA16 0x803E +#endif +#ifndef GL_COMPRESSED_ALPHA +#define GL_COMPRESSED_ALPHA 0x84E9 +#endif + +#ifndef GL_LUMINANCE +#define GL_LUMINANCE 0x1909 +#endif +#ifndef GL_LUMINANCE8 +#define GL_LUMINANCE8 0x8040 +#endif +#ifndef GL_LUMINANCE16 +#define GL_LUMINANCE16 0x8042 +#endif +#ifndef GL_COMPRESSED_LUMINANCE +#define GL_COMPRESSED_LUMINANCE 0x84EA +#endif + +#ifndef GL_LUMINANCE_ALPHA +#define GL_LUMINANCE_ALPHA 0x190A +#endif +#ifndef GL_LUMINANCE8_ALPHA8 +#define GL_LUMINANCE8_ALPHA8 0x8045 +#endif +#ifndef GL_LUMINANCE16_ALPHA16 +#define GL_LUMINANCE16_ALPHA16 0x8048 +#endif +#ifndef GL_COMPRESSED_LUMINANCE_ALPHA +#define GL_COMPRESSED_LUMINANCE_ALPHA 0x84EB +#endif + +// OpenGL 1.3 +#ifndef WIN32 +#define glActiveTexture_ glActiveTexture + +#define glBlendEquation_ glBlendEquation +#define glBlendColor_ glBlendColor + +#define glTexImage3D_ glTexImage3D +#define glTexSubImage3D_ glTexSubImage3D +#define glCopyTexSubImage3D_ glCopyTexSubImage3D + +#define glCompressedTexImage3D_ glCompressedTexImage3D +#define glCompressedTexImage2D_ glCompressedTexImage2D +#define glCompressedTexSubImage3D_ glCompressedTexSubImage3D +#define glCompressedTexSubImage2D_ glCompressedTexSubImage2D +#define glGetCompressedTexImage_ glGetCompressedTexImage + +#define glDrawRangeElements_ glDrawRangeElements +#else +extern PFNGLACTIVETEXTUREPROC glActiveTexture_; + +extern PFNGLBLENDEQUATIONPROC glBlendEquation_; +extern PFNGLBLENDCOLORPROC glBlendColor_; + +extern PFNGLTEXIMAGE3DPROC glTexImage3D_; +extern PFNGLTEXSUBIMAGE3DPROC glTexSubImage3D_; +extern PFNGLCOPYTEXSUBIMAGE3DPROC glCopyTexSubImage3D_; + +extern PFNGLCOMPRESSEDTEXIMAGE3DPROC glCompressedTexImage3D_; +extern PFNGLCOMPRESSEDTEXIMAGE2DPROC glCompressedTexImage2D_; +extern PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC glCompressedTexSubImage3D_; +extern PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glCompressedTexSubImage2D_; +extern PFNGLGETCOMPRESSEDTEXIMAGEPROC glGetCompressedTexImage_; + +extern PFNGLDRAWRANGEELEMENTSPROC glDrawRangeElements_; +#endif + +// OpenGL 2.0 +#ifdef __APPLE__ +#define glMultiDrawArrays_ glMultiDrawArrays +#define glMultiDrawElements_ glMultiDrawElements + +#define glBlendFuncSeparate_ glBlendFuncSeparate +#define glBlendEquationSeparate_ glBlendEquationSeparate +#define glStencilOpSeparate_ glStencilOpSeparate +#define glStencilFuncSeparate_ glStencilFuncSeparate +#define glStencilMaskSeparate_ glStencilMaskSeparate + +#define glGenBuffers_ glGenBuffers +#define glBindBuffer_ glBindBuffer +#define glMapBuffer_ glMapBuffer +#define glUnmapBuffer_ glUnmapBuffer +#define glBufferData_ glBufferData +#define glBufferSubData_ glBufferSubData +#define glDeleteBuffers_ glDeleteBuffers +#define glGetBufferSubData_ glGetBufferSubData + +#define glGenQueries_ glGenQueries +#define glDeleteQueries_ glDeleteQueries +#define glBeginQuery_ glBeginQuery +#define glEndQuery_ glEndQuery +#define glGetQueryiv_ glGetQueryiv +#define glGetQueryObjectiv_ glGetQueryObjectiv +#define glGetQueryObjectuiv_ glGetQueryObjectuiv + +#define glCreateProgram_ glCreateProgram +#define glDeleteProgram_ glDeleteProgram +#define glUseProgram_ glUseProgram +#define glCreateShader_ glCreateShader +#define glDeleteShader_ glDeleteShader +#define glShaderSource_ glShaderSource +#define glCompileShader_ glCompileShader +#define glGetShaderiv_ glGetShaderiv +#define glGetProgramiv_ glGetProgramiv +#define glAttachShader_ glAttachShader +#define glGetProgramInfoLog_ glGetProgramInfoLog +#define glGetShaderInfoLog_ glGetShaderInfoLog +#define glLinkProgram_ glLinkProgram +#define glGetUniformLocation_ glGetUniformLocation +#define glUniform1f_ glUniform1f +#define glUniform2f_ glUniform2f +#define glUniform3f_ glUniform3f +#define glUniform4f_ glUniform4f +#define glUniform1fv_ glUniform1fv +#define glUniform2fv_ glUniform2fv +#define glUniform3fv_ glUniform3fv +#define glUniform4fv_ glUniform4fv +#define glUniform1i_ glUniform1i +#define glUniform2i_ glUniform2i +#define glUniform3i_ glUniform3i +#define glUniform4i_ glUniform4i +#define glUniform1iv_ glUniform1iv +#define glUniform2iv_ glUniform2iv +#define glUniform3iv_ glUniform3iv +#define glUniform4iv_ glUniform4iv +#define glUniformMatrix2fv_ glUniformMatrix2fv +#define glUniformMatrix3fv_ glUniformMatrix3fv +#define glUniformMatrix4fv_ glUniformMatrix4fv +#define glBindAttribLocation_ glBindAttribLocation +#define glGetActiveUniform_ glGetActiveUniform +#define glEnableVertexAttribArray_ glEnableVertexAttribArray +#define glDisableVertexAttribArray_ glDisableVertexAttribArray + +#define glVertexAttrib1f_ glVertexAttrib1f +#define glVertexAttrib1fv_ glVertexAttrib1fv +#define glVertexAttrib1s_ glVertexAttrib1s +#define glVertexAttrib1sv_ glVertexAttrib1sv +#define glVertexAttrib2f_ glVertexAttrib2f +#define glVertexAttrib2fv_ glVertexAttrib2fv +#define glVertexAttrib2s_ glVertexAttrib2s +#define glVertexAttrib2sv_ glVertexAttrib2sv +#define glVertexAttrib3f_ glVertexAttrib3f +#define glVertexAttrib3fv_ glVertexAttrib3fv +#define glVertexAttrib3s_ glVertexAttrib3s +#define glVertexAttrib3sv_ glVertexAttrib3sv +#define glVertexAttrib4f_ glVertexAttrib4f +#define glVertexAttrib4fv_ glVertexAttrib4fv +#define glVertexAttrib4s_ glVertexAttrib4s +#define glVertexAttrib4sv_ glVertexAttrib4sv +#define glVertexAttrib4bv_ glVertexAttrib4bv +#define glVertexAttrib4iv_ glVertexAttrib4iv +#define glVertexAttrib4ubv_ glVertexAttrib4ubv +#define glVertexAttrib4uiv_ glVertexAttrib4uiv +#define glVertexAttrib4usv_ glVertexAttrib4usv +#define glVertexAttrib4Nbv_ glVertexAttrib4Nbv +#define glVertexAttrib4Niv_ glVertexAttrib4Niv +#define glVertexAttrib4Nub_ glVertexAttrib4Nub +#define glVertexAttrib4Nubv_ glVertexAttrib4Nubv +#define glVertexAttrib4Nuiv_ glVertexAttrib4Nuiv +#define glVertexAttrib4Nusv_ glVertexAttrib4Nusv +#define glVertexAttribPointer_ glVertexAttribPointer + +#define glDrawBuffers_ glDrawBuffers +#else +extern PFNGLMULTIDRAWARRAYSPROC glMultiDrawArrays_; +extern PFNGLMULTIDRAWELEMENTSPROC glMultiDrawElements_; + +extern PFNGLBLENDFUNCSEPARATEPROC glBlendFuncSeparate_; +extern PFNGLBLENDEQUATIONSEPARATEPROC glBlendEquationSeparate_; +extern PFNGLSTENCILOPSEPARATEPROC glStencilOpSeparate_; +extern PFNGLSTENCILFUNCSEPARATEPROC glStencilFuncSeparate_; +extern PFNGLSTENCILMASKSEPARATEPROC glStencilMaskSeparate_; + +extern PFNGLGENBUFFERSPROC glGenBuffers_; +extern PFNGLBINDBUFFERPROC glBindBuffer_; +extern PFNGLMAPBUFFERPROC glMapBuffer_; +extern PFNGLUNMAPBUFFERPROC glUnmapBuffer_; +extern PFNGLBUFFERDATAPROC glBufferData_; +extern PFNGLBUFFERSUBDATAPROC glBufferSubData_; +extern PFNGLDELETEBUFFERSPROC glDeleteBuffers_; +extern PFNGLGETBUFFERSUBDATAPROC glGetBufferSubData_; + +extern PFNGLGENQUERIESPROC glGenQueries_; +extern PFNGLDELETEQUERIESPROC glDeleteQueries_; +extern PFNGLBEGINQUERYPROC glBeginQuery_; +extern PFNGLENDQUERYPROC glEndQuery_; +extern PFNGLGETQUERYIVPROC glGetQueryiv_; +extern PFNGLGETQUERYOBJECTIVPROC glGetQueryObjectiv_; +extern PFNGLGETQUERYOBJECTUIVPROC glGetQueryObjectuiv_; + +extern PFNGLCREATEPROGRAMPROC glCreateProgram_; +extern PFNGLDELETEPROGRAMPROC glDeleteProgram_; +extern PFNGLUSEPROGRAMPROC glUseProgram_; +extern PFNGLCREATESHADERPROC glCreateShader_; +extern PFNGLDELETESHADERPROC glDeleteShader_; +extern PFNGLSHADERSOURCEPROC glShaderSource_; +extern PFNGLCOMPILESHADERPROC glCompileShader_; +extern PFNGLGETSHADERIVPROC glGetShaderiv_; +extern PFNGLGETPROGRAMIVPROC glGetProgramiv_; +extern PFNGLATTACHSHADERPROC glAttachShader_; +extern PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog_; +extern PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog_; +extern PFNGLLINKPROGRAMPROC glLinkProgram_; +extern PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation_; +extern PFNGLUNIFORM1FPROC glUniform1f_; +extern PFNGLUNIFORM2FPROC glUniform2f_; +extern PFNGLUNIFORM3FPROC glUniform3f_; +extern PFNGLUNIFORM4FPROC glUniform4f_; +extern PFNGLUNIFORM1FVPROC glUniform1fv_; +extern PFNGLUNIFORM2FVPROC glUniform2fv_; +extern PFNGLUNIFORM3FVPROC glUniform3fv_; +extern PFNGLUNIFORM4FVPROC glUniform4fv_; +extern PFNGLUNIFORM1IPROC glUniform1i_; +extern PFNGLUNIFORM2IPROC glUniform2i_; +extern PFNGLUNIFORM3IPROC glUniform3i_; +extern PFNGLUNIFORM4IPROC glUniform4i_; +extern PFNGLUNIFORM1IVPROC glUniform1iv_; +extern PFNGLUNIFORM2IVPROC glUniform2iv_; +extern PFNGLUNIFORM3IVPROC glUniform3iv_; +extern PFNGLUNIFORM4IVPROC glUniform4iv_; +extern PFNGLUNIFORMMATRIX2FVPROC glUniformMatrix2fv_; +extern PFNGLUNIFORMMATRIX3FVPROC glUniformMatrix3fv_; +extern PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv_; +extern PFNGLBINDATTRIBLOCATIONPROC glBindAttribLocation_; +extern PFNGLGETACTIVEUNIFORMPROC glGetActiveUniform_; +extern PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray_; +extern PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray_; + +extern PFNGLVERTEXATTRIB1FPROC glVertexAttrib1f_; +extern PFNGLVERTEXATTRIB1FVPROC glVertexAttrib1fv_; +extern PFNGLVERTEXATTRIB1SPROC glVertexAttrib1s_; +extern PFNGLVERTEXATTRIB1SVPROC glVertexAttrib1sv_; +extern PFNGLVERTEXATTRIB2FPROC glVertexAttrib2f_; +extern PFNGLVERTEXATTRIB2FVPROC glVertexAttrib2fv_; +extern PFNGLVERTEXATTRIB2SPROC glVertexAttrib2s_; +extern PFNGLVERTEXATTRIB2SVPROC glVertexAttrib2sv_; +extern PFNGLVERTEXATTRIB3FPROC glVertexAttrib3f_; +extern PFNGLVERTEXATTRIB3FVPROC glVertexAttrib3fv_; +extern PFNGLVERTEXATTRIB3SPROC glVertexAttrib3s_; +extern PFNGLVERTEXATTRIB3SVPROC glVertexAttrib3sv_; +extern PFNGLVERTEXATTRIB4FPROC glVertexAttrib4f_; +extern PFNGLVERTEXATTRIB4FVPROC glVertexAttrib4fv_; +extern PFNGLVERTEXATTRIB4SPROC glVertexAttrib4s_; +extern PFNGLVERTEXATTRIB4SVPROC glVertexAttrib4sv_; +extern PFNGLVERTEXATTRIB4BVPROC glVertexAttrib4bv_; +extern PFNGLVERTEXATTRIB4IVPROC glVertexAttrib4iv_; +extern PFNGLVERTEXATTRIB4UBVPROC glVertexAttrib4ubv_; +extern PFNGLVERTEXATTRIB4UIVPROC glVertexAttrib4uiv_; +extern PFNGLVERTEXATTRIB4USVPROC glVertexAttrib4usv_; +extern PFNGLVERTEXATTRIB4NBVPROC glVertexAttrib4Nbv_; +extern PFNGLVERTEXATTRIB4NIVPROC glVertexAttrib4Niv_; +extern PFNGLVERTEXATTRIB4NUBPROC glVertexAttrib4Nub_; +extern PFNGLVERTEXATTRIB4NUBVPROC glVertexAttrib4Nubv_; +extern PFNGLVERTEXATTRIB4NUIVPROC glVertexAttrib4Nuiv_; +extern PFNGLVERTEXATTRIB4NUSVPROC glVertexAttrib4Nusv_; +extern PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer_; + +extern PFNGLDRAWBUFFERSPROC glDrawBuffers_; +#endif + +#ifndef GL_VERSION_2_1 +#define GL_VERSION_2_1 1 +#define GL_PIXEL_PACK_BUFFER 0x88EB +#define GL_PIXEL_UNPACK_BUFFER 0x88EC +#endif + +#ifndef GL_EXT_texture_filter_anisotropic +#define GL_EXT_texture_filter_anisotropic 1 +#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE +#define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF +#endif + +#ifndef GL_EXT_texture_compression_s3tc +#define GL_EXT_texture_compression_s3tc 1 +#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 +#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 +#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 +#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 +#endif + +#ifndef GL_ARB_framebuffer_object +#define GL_ARB_framebuffer_object 1 +#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A +#define GL_DEPTH_STENCIL 0x84F9 +#define GL_UNSIGNED_INT_24_8 0x84FA +#define GL_DEPTH24_STENCIL8 0x88F0 +#define GL_FRAMEBUFFER_BINDING 0x8CA6 +#define GL_READ_FRAMEBUFFER 0x8CA8 +#define GL_DRAW_FRAMEBUFFER 0x8CA9 +#define GL_FRAMEBUFFER_COMPLETE 0x8CD5 +#define GL_COLOR_ATTACHMENT0 0x8CE0 +#define GL_COLOR_ATTACHMENT1 0x8CE1 +#define GL_DEPTH_ATTACHMENT 0x8D00 +#define GL_STENCIL_ATTACHMENT 0x8D20 +#define GL_FRAMEBUFFER 0x8D40 +#define GL_RENDERBUFFER 0x8D41 +typedef void (APIENTRYP PFNGLBINDRENDERBUFFERPROC) (GLenum target, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLDELETERENDERBUFFERSPROC) (GLsizei n, const GLuint *renderbuffers); +typedef void (APIENTRYP PFNGLGENRENDERBUFFERSPROC) (GLsizei n, GLuint *renderbuffers); +typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLBINDFRAMEBUFFERPROC) (GLenum target, GLuint framebuffer); +typedef void (APIENTRYP PFNGLDELETEFRAMEBUFFERSPROC) (GLsizei n, const GLuint *framebuffers); +typedef void (APIENTRYP PFNGLGENFRAMEBUFFERSPROC) (GLsizei n, GLuint *framebuffers); +typedef GLenum (APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSPROC) (GLenum target); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFERPROC) (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLGENERATEMIPMAPPROC) (GLenum target); +typedef void (APIENTRYP PFNGLBLITFRAMEBUFFERPROC) (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +#endif + +// GL_EXT_framebuffer_object +extern PFNGLBINDRENDERBUFFERPROC glBindRenderbuffer_; +extern PFNGLDELETERENDERBUFFERSPROC glDeleteRenderbuffers_; +extern PFNGLGENFRAMEBUFFERSPROC glGenRenderbuffers_; +extern PFNGLRENDERBUFFERSTORAGEPROC glRenderbufferStorage_; +extern PFNGLCHECKFRAMEBUFFERSTATUSPROC glCheckFramebufferStatus_; +extern PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer_; +extern PFNGLDELETEFRAMEBUFFERSPROC glDeleteFramebuffers_; +extern PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers_; +extern PFNGLFRAMEBUFFERTEXTURE2DPROC glFramebufferTexture2D_; +extern PFNGLFRAMEBUFFERRENDERBUFFERPROC glFramebufferRenderbuffer_; +extern PFNGLGENERATEMIPMAPPROC glGenerateMipmap_; + +// GL_EXT_framebuffer_blit +extern PFNGLBLITFRAMEBUFFERPROC glBlitFramebuffer_; + +#ifndef GL_ARB_texture_rg +#define GL_ARB_texture_rg 1 +#define GL_RG 0x8227 +#define GL_R8 0x8229 +#define GL_R16 0x822A +#define GL_RG8 0x822B +#define GL_RG16 0x822C +#define GL_R16F 0x822D +#define GL_R32F 0x822E +#define GL_RG16F 0x822F +#define GL_RG32F 0x8230 +#endif + +#ifndef GL_EXT_texture_compression_latc +#define GL_EXT_texture_compression_latc 1 +#define GL_COMPRESSED_LUMINANCE_LATC1_EXT 0x8C70 +#define GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT 0x8C72 +#endif + +#ifndef GL_ARB_texture_compression_rgtc +#define GL_ARB_texture_compression_rgtc 1 +#define GL_COMPRESSED_RED_RGTC1 0x8DBB +#define GL_COMPRESSED_RG_RGTC2 0x8DBD +#endif + +#ifndef GL_ARB_map_buffer_range +#define GL_ARB_map_buffer_range 1 +#define GL_MAP_READ_BIT 0x0001 +#define GL_MAP_WRITE_BIT 0x0002 +#define GL_MAP_INVALIDATE_RANGE_BIT 0x0004 +#define GL_MAP_INVALIDATE_BUFFER_BIT 0x0008 +#define GL_MAP_FLUSH_EXPLICIT_BIT 0x0010 +#define GL_MAP_UNSYNCHRONIZED_BIT 0x0020 +typedef GLvoid* (APIENTRYP PFNGLMAPBUFFERRANGEPROC) (GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access); +typedef void (APIENTRYP PFNGLFLUSHMAPPEDBUFFERRANGEPROC) (GLenum target, GLintptr offset, GLsizeiptr length); +#endif +extern PFNGLMAPBUFFERRANGEPROC glMapBufferRange_; +extern PFNGLFLUSHMAPPEDBUFFERRANGEPROC glFlushMappedBufferRange_; + +#ifndef GL_ARB_uniform_buffer_object +#define GL_ARB_uniform_buffer_object 1 +#define GL_UNIFORM_BUFFER 0x8A11 +#define GL_UNIFORM_BUFFER_BINDING 0x8A28 +#define GL_UNIFORM_BUFFER_START 0x8A29 +#define GL_UNIFORM_BUFFER_SIZE 0x8A2A +#define GL_MAX_VERTEX_UNIFORM_BLOCKS 0x8A2B +#define GL_MAX_GEOMETRY_UNIFORM_BLOCKS 0x8A2C +#define GL_MAX_FRAGMENT_UNIFORM_BLOCKS 0x8A2D +#define GL_MAX_COMBINED_UNIFORM_BLOCKS 0x8A2E +#define GL_MAX_UNIFORM_BUFFER_BINDINGS 0x8A2F +#define GL_MAX_UNIFORM_BLOCK_SIZE 0x8A30 +#define GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS 0x8A31 +#define GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS 0x8A32 +#define GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS 0x8A33 +#define GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT 0x8A34 +#define GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH 0x8A35 +#define GL_ACTIVE_UNIFORM_BLOCKS 0x8A36 +#define GL_UNIFORM_TYPE 0x8A37 +#define GL_UNIFORM_SIZE 0x8A38 +#define GL_UNIFORM_NAME_LENGTH 0x8A39 +#define GL_UNIFORM_BLOCK_INDEX 0x8A3A +#define GL_UNIFORM_OFFSET 0x8A3B +#define GL_UNIFORM_ARRAY_STRIDE 0x8A3C +#define GL_UNIFORM_MATRIX_STRIDE 0x8A3D +#define GL_UNIFORM_IS_ROW_MAJOR 0x8A3E +#define GL_UNIFORM_BLOCK_BINDING 0x8A3F +#define GL_UNIFORM_BLOCK_DATA_SIZE 0x8A40 +#define GL_UNIFORM_BLOCK_NAME_LENGTH 0x8A41 +#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS 0x8A42 +#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES 0x8A43 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER 0x8A44 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER 0x8A45 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER 0x8A46 +#define GL_INVALID_INDEX 0xFFFFFFFFu +typedef void (APIENTRYP PFNGLGETUNIFORMINDICESPROC) (GLuint program, GLsizei uniformCount, const GLchar* *uniformNames, GLuint *uniformIndices); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMSIVPROC) (GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params); +typedef GLuint (APIENTRYP PFNGLGETUNIFORMBLOCKINDEXPROC) (GLuint program, const GLchar *uniformBlockName); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMBLOCKIVPROC) (GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLUNIFORMBLOCKBINDINGPROC) (GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding); +#endif +#ifndef GL_INVALID_INDEX +#define GL_INVALID_INDEX 0xFFFFFFFFu +#endif +extern PFNGLGETUNIFORMINDICESPROC glGetUniformIndices_; +extern PFNGLGETACTIVEUNIFORMSIVPROC glGetActiveUniformsiv_; +extern PFNGLGETUNIFORMBLOCKINDEXPROC glGetUniformBlockIndex_; +extern PFNGLGETACTIVEUNIFORMBLOCKIVPROC glGetActiveUniformBlockiv_; +extern PFNGLUNIFORMBLOCKBINDINGPROC glUniformBlockBinding_; + +#ifndef GL_VERSION_3_0 +#define GL_VERSION_3_0 1 +#define GL_NUM_EXTENSIONS 0x821D +#define GL_COMPARE_REF_TO_TEXTURE 0x884E +#define GL_MAX_VARYING_COMPONENTS 0x8B4B +#define GL_RGBA32F 0x8814 +#define GL_RGB32F 0x8815 +#define GL_RGBA16F 0x881A +#define GL_RGB16F 0x881B +#define GL_COMPRESSED_RED 0x8225 +#define GL_COMPRESSED_RG 0x8226 +typedef void (APIENTRYP PFNGLBINDBUFFERRANGEPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLBINDBUFFERBASEPROC) (GLenum target, GLuint index, GLuint buffer); +typedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONPROC) (GLuint program, GLuint color, const GLchar *name); +typedef const GLubyte * (APIENTRYP PFNGLGETSTRINGIPROC) (GLenum name, GLuint index); +#elif GL_GLEXT_VERSION < 43 +typedef void (APIENTRYP PFNGLBINDBUFFERRANGEPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLBINDBUFFERBASEPROC) (GLenum target, GLuint index, GLuint buffer); +#endif + +extern PFNGLGETSTRINGIPROC glGetStringi_; +extern PFNGLBINDFRAGDATALOCATIONPROC glBindFragDataLocation_; +extern PFNGLBINDBUFFERBASEPROC glBindBufferBase_; +extern PFNGLBINDBUFFERRANGEPROC glBindBufferRange_; + +#ifndef GL_VERSION_3_1 +#define GL_VERSION_3_1 1 +#define GL_TEXTURE_RECTANGLE 0x84F5 +#endif + +#ifndef GL_ARB_vertex_array_object +#define GL_ARB_vertex_array_object 1 +#define GL_VERTEX_ARRAY_BINDING 0x85B5 +typedef void (APIENTRYP PFNGLBINDVERTEXARRAYPROC) (GLuint array); +typedef void (APIENTRYP PFNGLDELETEVERTEXARRAYSPROC) (GLsizei n, const GLuint *arrays); +typedef void (APIENTRYP PFNGLGENVERTEXARRAYSPROC) (GLsizei n, GLuint *arrays); +typedef GLboolean (APIENTRYP PFNGLISVERTEXARRAYPROC) (GLuint array); +#endif +extern PFNGLBINDVERTEXARRAYPROC glBindVertexArray_; +extern PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArrays_; +extern PFNGLGENVERTEXARRAYSPROC glGenVertexArrays_; +extern PFNGLISVERTEXARRAYPROC glIsVertexArray_; + +#ifndef GL_ARB_texture_swizzle +#define GL_ARB_texture_swizzle 1 +#define GL_TEXTURE_SWIZZLE_R 0x8E42 +#define GL_TEXTURE_SWIZZLE_G 0x8E43 +#define GL_TEXTURE_SWIZZLE_B 0x8E44 +#define GL_TEXTURE_SWIZZLE_A 0x8E45 +#define GL_TEXTURE_SWIZZLE_RGBA 0x8E46 +#endif + diff --git a/src/shared/iengine.h b/src/shared/iengine.h new file mode 100644 index 0000000..32b1957 --- /dev/null +++ b/src/shared/iengine.h @@ -0,0 +1,583 @@ +// the interface the game uses to access the engine + +extern int curtime; // current frame time +extern int lastmillis; // last time +extern int elapsedtime; // elapsed frame time +extern int totalmillis; // total elapsed time +extern uint totalsecs; +extern int gamespeed, paused; + +enum +{ + MATF_INDEX_SHIFT = 0, + MATF_VOLUME_SHIFT = 2, + MATF_CLIP_SHIFT = 5, + MATF_FLAG_SHIFT = 8, + + MATF_INDEX = 3 << MATF_INDEX_SHIFT, + MATF_VOLUME = 7 << MATF_VOLUME_SHIFT, + MATF_CLIP = 7 << MATF_CLIP_SHIFT, + MATF_FLAGS = 0xFF << MATF_FLAG_SHIFT +}; + +enum // cube empty-space materials +{ + MAT_AIR = 0, // the default, fill the empty space with air + MAT_WATER = 1 << MATF_VOLUME_SHIFT, // fill with water, showing waves at the surface + MAT_LAVA = 2 << MATF_VOLUME_SHIFT, // fill with lava + MAT_GLASS = 3 << MATF_VOLUME_SHIFT, // behaves like clip but is blended blueish + + MAT_NOCLIP = 1 << MATF_CLIP_SHIFT, // collisions always treat cube as empty + MAT_CLIP = 2 << MATF_CLIP_SHIFT, // collisions always treat cube as solid + MAT_GAMECLIP = 3 << MATF_CLIP_SHIFT, // game specific clip material + + MAT_DEATH = 1 << MATF_FLAG_SHIFT, // force player suicide + MAT_ALPHA = 4 << MATF_FLAG_SHIFT // alpha blended +}; + +#define isliquid(mat) ((mat)==MAT_WATER || (mat)==MAT_LAVA) +#define isclipped(mat) ((mat)==MAT_GLASS) +#define isdeadly(mat) ((mat)==MAT_LAVA) + +extern void lightent(extentity &e, float height = 8.0f); +extern void lightreaching(const vec &target, vec &color, vec &dir, bool fast = false, extentity *e = 0, float ambient = 0.4f); +extern entity *brightestlight(const vec &target, const vec &dir); + +enum { RAY_BB = 1, RAY_POLY = 3, RAY_ALPHAPOLY = 7, RAY_ENTS = 9, RAY_CLIPMAT = 16, RAY_SKIPFIRST = 32, RAY_EDITMAT = 64, RAY_SHADOW = 128, RAY_PASS = 256, RAY_SKIPSKY = 512, RAY_SKYTEX = 1024 }; + +extern float raycube (const vec &o, const vec &ray, float radius = 0, int mode = RAY_CLIPMAT, int size = 0, extentity *t = 0); +extern float raycubepos(const vec &o, const vec &ray, vec &hit, float radius = 0, int mode = RAY_CLIPMAT, int size = 0); +extern float rayfloor (const vec &o, vec &floor, int mode = 0, float radius = 0); +extern bool raycubelos(const vec &o, const vec &dest, vec &hitpos); + +extern int thirdperson; +extern bool isthirdperson(); + +extern bool settexture(const char *name, int clamp = 0); + +// octaedit + +enum { EDIT_FACE = 0, EDIT_TEX, EDIT_MAT, EDIT_FLIP, EDIT_COPY, EDIT_PASTE, EDIT_ROTATE, EDIT_REPLACE, EDIT_DELCUBE, EDIT_REMIP, EDIT_VSLOT, EDIT_UNDO, EDIT_REDO }; + +struct selinfo +{ + int corner; + int cx, cxs, cy, cys; + ivec o, s; + int grid, orient; + selinfo() : corner(0), cx(0), cxs(0), cy(0), cys(0), o(0, 0, 0), s(0, 0, 0), grid(8), orient(0) {} + int size() const { return s.x*s.y*s.z; } + int us(int d) const { return s[d]*grid; } + bool operator==(const selinfo &sel) const { return o==sel.o && s==sel.s && grid==sel.grid && orient==sel.orient; } + bool validate() + { + extern int worldsize; + if(grid <= 0 || grid >= worldsize) return false; + if(o.x >= worldsize || o.y >= worldsize || o.z >= worldsize) return false; + if(o.x < 0) { s.x -= (grid - 1 - o.x)/grid; o.x = 0; } + if(o.y < 0) { s.y -= (grid - 1 - o.y)/grid; o.y = 0; } + if(o.z < 0) { s.z -= (grid - 1 - o.z)/grid; o.z = 0; } + s.x = clamp(s.x, 0, (worldsize - o.x)/grid); + s.y = clamp(s.y, 0, (worldsize - o.y)/grid); + s.z = clamp(s.z, 0, (worldsize - o.z)/grid); + return s.x > 0 && s.y > 0 && s.z > 0; + } +}; + +struct editinfo; +extern editinfo *localedit; + +extern bool editmode; + +extern int shouldpacktex(int index); +extern bool packeditinfo(editinfo *e, int &inlen, uchar *&outbuf, int &outlen); +extern bool unpackeditinfo(editinfo *&e, const uchar *inbuf, int inlen, int outlen); +extern void freeeditinfo(editinfo *&e); +extern void pruneundos(int maxremain = 0); +extern bool packundo(int op, int &inlen, uchar *&outbuf, int &outlen); +extern bool unpackundo(const uchar *inbuf, int inlen, int outlen); +extern bool noedit(bool view = false, bool msg = true); +extern void toggleedit(bool force = true); +extern void mpeditface(int dir, int mode, selinfo &sel, bool local); +extern void mpedittex(int tex, int allfaces, selinfo &sel, bool local); +extern bool mpedittex(int tex, int allfaces, selinfo &sel, ucharbuf &buf); +extern void mpeditmat(int matid, int filter, selinfo &sel, bool local); +extern void mpflip(selinfo &sel, bool local); +extern void mpcopy(editinfo *&e, selinfo &sel, bool local); +extern void mppaste(editinfo *&e, selinfo &sel, bool local); +extern void mprotate(int cw, selinfo &sel, bool local); +extern void mpreplacetex(int oldtex, int newtex, bool insel, selinfo &sel, bool local); +extern bool mpreplacetex(int oldtex, int newtex, bool insel, selinfo &sel, ucharbuf &buf); +extern void mpdelcube(selinfo &sel, bool local); +extern bool mpeditvslot(int delta, int allfaces, selinfo &sel, ucharbuf &buf); +extern void mpremip(bool local); + +// texture + +struct VSlot; + +extern void packvslot(vector<uchar> &buf, int index); +extern void packvslot(vector<uchar> &buf, const VSlot *vs); + +// command +extern int variable(const char *name, int min, int cur, int max, int *storage, identfun fun, int flags); +extern float fvariable(const char *name, float min, float cur, float max, float *storage, identfun fun, int flags); +extern char *svariable(const char *name, const char *cur, char **storage, identfun fun, int flags); +extern void setvar(const char *name, int i, bool dofunc = true, bool doclamp = true); +extern void setfvar(const char *name, float f, bool dofunc = true, bool doclamp = true); +extern void setsvar(const char *name, const char *str, bool dofunc = true); +extern void setvarchecked(ident *id, int val); +extern void setfvarchecked(ident *id, float val); +extern void setsvarchecked(ident *id, const char *val); +extern void touchvar(const char *name); +extern int getvar(const char *name); +extern int getvarmin(const char *name); +extern int getvarmax(const char *name); +extern bool identexists(const char *name); +extern ident *getident(const char *name); +extern ident *newident(const char *name, int flags = 0); +extern ident *readident(const char *name); +extern ident *writeident(const char *name, int flags = 0); +extern bool addcommand(const char *name, identfun fun, const char *narg); +extern bool addkeyword(int type, const char *name); +extern uint *compilecode(const char *p); +extern void keepcode(uint *p); +extern void freecode(uint *p); +extern void executeret(const uint *code, tagval &result = *commandret); +extern void executeret(const char *p, tagval &result = *commandret); +extern void executeret(ident *id, tagval *args, int numargs, bool lookup = false, tagval &result = *commandret); +extern char *executestr(const uint *code); +extern char *executestr(const char *p); +extern char *executestr(ident *id, tagval *args, int numargs, bool lookup = false); +extern char *execidentstr(const char *name, bool lookup = false); +extern int execute(const uint *code); +extern int execute(const char *p); +extern int execute(ident *id, tagval *args, int numargs, bool lookup = false); +extern int execident(const char *name, int noid = 0, bool lookup = false); +extern bool executebool(const uint *code); +extern bool executebool(const char *p); +extern bool executebool(ident *id, tagval *args, int numargs, bool lookup = false); +extern bool execidentbool(const char *name, bool noid = false, bool lookup = false); +extern bool execfile(const char *cfgfile, bool msg = true); +extern void alias(const char *name, const char *action); +extern void alias(const char *name, tagval &v); +extern const char *getalias(const char *name); +extern const char *escapestring(const char *s); +extern const char *escapeid(const char *s); +static inline const char *escapeid(ident &id) { return escapeid(id.name); } +extern bool validateblock(const char *s); +extern void explodelist(const char *s, vector<char *> &elems, int limit = -1); +extern char *indexlist(const char *s, int pos); +extern int listlen(const char *s); +extern void printvar(ident *id); +extern void printvar(ident *id, int i); +extern void printfvar(ident *id, float f); +extern void printsvar(ident *id, const char *s); +extern int clampvar(ident *id, int i, int minval, int maxval); +extern float clampfvar(ident *id, float f, float minval, float maxval); +extern void loopiter(ident *id, identstack &stack, const tagval &v); +extern void loopend(ident *id, identstack &stack); + +#define loopstart(id, stack) if((id)->type != ID_ALIAS) return; identstack stack; +static inline void loopiter(ident *id, identstack &stack, int i) { tagval v; v.setint(i); loopiter(id, stack, v); } +static inline void loopiter(ident *id, identstack &stack, float f) { tagval v; v.setfloat(f); loopiter(id, stack, v); } +static inline void loopiter(ident *id, identstack &stack, const char *s) { tagval v; v.setstr(newstring(s)); loopiter(id, stack, v); } + +// console + +enum +{ + CON_INFO = 1<<0, + CON_WARN = 1<<1, + CON_ERROR = 1<<2, + CON_DEBUG = 1<<3, + CON_INIT = 1<<4, + CON_ECHO = 1<<5, + + CON_FLAGS = 0xFFFF, + CON_TAG_SHIFT = 16, + CON_TAG_MASK = (0x7FFF << CON_TAG_SHIFT) +}; + +extern void conoutf(const char *s, ...) PRINTFARGS(1, 2); +extern void conoutf(int type, const char *s, ...) PRINTFARGS(2, 3); +extern void conoutf(int type, int tag, const char *s, ...) PRINTFARGS(3, 4); +extern void conoutfv(int type, const char *fmt, va_list args); + +extern FILE *getlogfile(); +extern void setlogfile(const char *fname); +extern void closelogfile(); +extern void logoutfv(const char *fmt, va_list args); +extern void logoutf(const char *fmt, ...) PRINTFARGS(1, 2); + +// menus +extern vec menuinfrontofplayer(); +extern void newgui(char *name, char *contents, char *header = NULL, char *init = NULL); +extern void showgui(const char *name); +extern int cleargui(int n = 0); + +// octa +extern int lookupmaterial(const vec &o); + +static inline bool insideworld(const vec &o) +{ + extern int worldsize; + return o.x>=0 && o.x<worldsize && o.y>=0 && o.y<worldsize && o.z>=0 && o.z<worldsize; +} + +static inline bool insideworld(const ivec &o) +{ + extern int worldsize; + return uint(o.x)<uint(worldsize) && uint(o.y)<uint(worldsize) && uint(o.z)<uint(worldsize); +} + +// world +extern bool emptymap(int factor, bool force, const char *mname = "", bool usecfg = true); +extern bool enlargemap(bool force); +extern int findentity(int type, int index = 0, int attr1 = -1, int attr2 = -1); +extern void findents(int low, int high, bool notspawned, const vec &pos, const vec &radius, vector<int> &found); +extern void mpeditent(int i, const vec &o, int type, int attr1, int attr2, int attr3, int attr4, int attr5, bool local); +extern vec getselpos(); +extern int getworldsize(); +extern int getmapversion(); +extern void renderentcone(const extentity &e, const vec &dir, float radius, float angle); +extern void renderentarrow(const extentity &e, const vec &dir, float radius); +extern void renderentattachment(const extentity &e); +extern void renderentsphere(const extentity &e, float radius); +extern void renderentring(const extentity &e, float radius, int axis = 0); + +// main +extern void fatal(const char *s, ...) PRINTFARGS(1, 2); + +// rendertext +extern bool setfont(const char *name); +extern void pushfont(); +extern bool popfont(); +extern void gettextres(int &w, int &h); +extern void draw_text(const char *str, int left, int top, int r = 255, int g = 255, int b = 255, int a = 255, int cursor = -1, int maxwidth = -1); +extern void draw_textf(const char *fstr, int left, int top, ...) PRINTFARGS(1, 4); +extern float text_widthf(const char *str); +extern void text_boundsf(const char *str, float &width, float &height, int maxwidth = -1); +extern int text_visible(const char *str, float hitx, float hity, int maxwidth); +extern void text_posf(const char *str, int cursor, float &cx, float &cy, int maxwidth); + +static inline int text_width(const char *str) +{ + return int(ceil(text_widthf(str))); +} + +static inline void text_bounds(const char *str, int &width, int &height, int maxwidth = -1) +{ + float widthf, heightf; + text_boundsf(str, widthf, heightf, maxwidth); + width = int(ceil(widthf)); + height = int(ceil(heightf)); +} + +static inline void text_pos(const char *str, int cursor, int &cx, int &cy, int maxwidth) +{ + float cxf, cyf; + text_posf(str, cursor, cxf, cyf, maxwidth); + cx = int(cxf); + cy = int(cyf); +} + +// renderva +enum +{ + DL_SHRINK = 1<<0, + DL_EXPAND = 1<<1, + DL_FLASH = 1<<2 +}; + +extern void adddynlight(const vec &o, float radius, const vec &color, int fade = 0, int peak = 0, int flags = 0, float initradius = 0, const vec &initcolor = vec(0, 0, 0), physent *owner = NULL); +extern void dynlightreaching(const vec &target, vec &color, vec &dir, bool hud = false); +extern void removetrackeddynlights(physent *owner = NULL); + +// rendergl +extern physent *camera1; +extern vec worldpos, camdir, camright, camup; + +extern void disablezoom(); + +extern vec calcavatarpos(const vec &pos, float dist); +extern vec calcmodelpreviewpos(const vec &radius, float &yaw); + +extern void damageblend(int n); +extern void damagecompass(int n, const vec &loc); +extern void cleardamagescreen(); + +extern vec minimapcenter, minimapradius, minimapscale; +extern void bindminimap(); + +extern matrix4 hudmatrix; +extern void resethudmatrix(); +extern void pushhudmatrix(); +extern void flushhudmatrix(bool flushparams = true); +extern void pophudmatrix(bool flush = true, bool flushparams = true); +extern void pushhudscale(float sx, float sy = 0); +extern void pushhudtranslate(float tx, float ty, float sx = 0, float sy = 0); + +// renderparticles +enum +{ + PART_BLOOD = 0, + PART_WATER, + PART_SMOKE, + PART_STEAM, + PART_FLAME, + PART_FIREBALL1, PART_FIREBALL2, PART_FIREBALL3, + PART_STREAK, PART_LIGHTNING, + PART_EXPLOSION, PART_EXPLOSION_BLUE, + PART_SPARK, PART_EDIT, + PART_SNOW, + PART_MUZZLE_FLASH1, PART_MUZZLE_FLASH2, PART_MUZZLE_FLASH3, + PART_HUD_ICON, + PART_HUD_ICON_GREY, + PART_TEXT, + PART_TEXT_ICON, + PART_METER, PART_METER_VS, + PART_LENS_FLARE +}; + +extern bool canaddparticles(); +extern void regular_particle_splash(int type, int num, int fade, const vec &p, int color = 0xFFFFFF, float size = 1.0f, int radius = 150, int gravity = 2, int delay = 0); +extern void regular_particle_flame(int type, const vec &p, float radius, float height, int color, int density = 3, float scale = 2.0f, float speed = 200.0f, float fade = 600.0f, int gravity = -15); +extern void particle_splash(int type, int num, int fade, const vec &p, int color = 0xFFFFFF, float size = 1.0f, int radius = 150, int gravity = 2); +extern void particle_trail(int type, int fade, const vec &from, const vec &to, int color = 0xFFFFFF, float size = 1.0f, int gravity = 20); +extern void particle_text(const vec &s, const char *t, int type, int fade = 2000, int color = 0xFFFFFF, float size = 2.0f, int gravity = 0, int offset = 0); +extern void particle_textcopy(const vec &s, const char *t, int type, int fade = 2000, int color = 0xFFFFFF, float size = 2.0f, int gravity = 0); +extern void particle_texticon(const vec &s, int ix, int iy, float offset, int type, int fade = 2000, int color = 0xFFFFFF, float size = 2.0f, int gravity = 0); +extern void particle_icon(const vec &s, int ix, int iy, int type, int fade = 2000, int color = 0xFFFFFF, float size = 2.0f, int gravity = 0); +extern void particle_meter(const vec &s, float val, int type, int fade = 1, int color = 0xFFFFFF, int color2 = 0xFFFFF, float size = 2.0f); +extern void particle_flare(const vec &p, const vec &dest, int fade, int type, int color = 0xFFFFFF, float size = 0.28f, physent *owner = NULL); +extern void particle_fireball(const vec &dest, float max, int type, int fade = -1, int color = 0xFFFFFF, float size = 4.0f); +extern void removetrackedparticles(physent *owner = NULL); + +// decal +enum +{ + DECAL_SCORCH = 0, + DECAL_BLOOD, + DECAL_BULLET +}; + +extern void adddecal(int type, const vec ¢er, const vec &surface, float radius, const bvec &color = bvec(0xFF, 0xFF, 0xFF), int info = 0); + +// worldio +extern bool load_world(const char *mname, const char *cname = NULL); +extern bool save_world(const char *mname, bool nolms = false); +extern void fixmapname(char *name); +extern void getmapfilenames(const char *fname, const char *cname, char *pakname, char *mapname, char *cfgname); +extern uint getmapcrc(); +extern void clearmapcrc(); +extern bool loadents(const char *fname, vector<entity> &ents, uint *crc = NULL); + +// physics +extern vec collidewall; +extern int collideinside; +extern physent *collideplayer; + +extern void moveplayer(physent *pl, int moveres, bool local); +extern bool moveplayer(physent *pl, int moveres, bool local, int curtime); +extern bool collide(physent *d, const vec &dir = vec(0, 0, 0), float cutoff = 0.0f, bool playercol = true, bool insideplayercol = false); +extern bool bounce(physent *d, float secs, float elasticity, float waterfric, float grav); +extern bool bounce(physent *d, float elasticity, float waterfric, float grav); +extern void avoidcollision(physent *d, const vec &dir, physent *obstacle, float space); +extern bool overlapsdynent(const vec &o, float radius); +extern bool movecamera(physent *pl, const vec &dir, float dist, float stepdist); +extern void physicsframe(); +extern void dropenttofloor(entity *e); +extern bool droptofloor(vec &o, float radius, float height); + +extern void vecfromyawpitch(float yaw, float pitch, int move, int strafe, vec &m); +extern void vectoyawpitch(const vec &v, float &yaw, float &pitch); +extern bool moveplatform(physent *p, const vec &dir); +extern void updatephysstate(physent *d); +extern void cleardynentcache(); +extern void updatedynentcache(physent *d); +extern bool entinmap(dynent *d, bool avoidplayers = false); +extern void findplayerspawn(dynent *d, int forceent = -1, int tag = 0); + +// sound +enum +{ + SND_MAP = 1<<0, + SND_NO_ALT = 1<<1, + SND_USE_ALT = 1<<2 +}; + +extern int playsound(int n, const vec *loc = NULL, extentity *ent = NULL, int flags = 0, int loops = 0, int fade = 0, int chanid = -1, int radius = 0, int expire = -1); +extern int playsoundname(const char *s, const vec *loc = NULL, int vol = 0, int flags = 0, int loops = 0, int fade = 0, int chanid = -1, int radius = 0, int expire = -1); +extern void preloadsound(int n); +extern void preloadmapsound(int n); +extern bool stopsound(int n, int chanid, int fade = 0); +extern void stopsounds(); +extern void initsound(); + +// rendermodel +enum { MDL_CULL_VFC = 1<<0, MDL_CULL_DIST = 1<<1, MDL_CULL_OCCLUDED = 1<<2, MDL_CULL_QUERY = 1<<3, MDL_SHADOW = 1<<4, MDL_DYNSHADOW = 1<<5, MDL_LIGHT = 1<<6, MDL_DYNLIGHT = 1<<7, MDL_FULLBRIGHT = 1<<8, MDL_NORENDER = 1<<9, MDL_LIGHT_FAST = 1<<10, MDL_HUD = 1<<11, MDL_GHOST = 1<<12 }; + +struct model; +struct modelattach +{ + const char *tag, *name; + int anim, basetime; + vec *pos; + model *m; + + modelattach() : tag(NULL), name(NULL), anim(-1), basetime(0), pos(NULL), m(NULL) {} + modelattach(const char *tag, const char *name, int anim = -1, int basetime = 0) : tag(tag), name(name), anim(anim), basetime(basetime), pos(NULL), m(NULL) {} + modelattach(const char *tag, vec *pos) : tag(tag), name(NULL), anim(-1), basetime(0), pos(pos), m(NULL) {} +}; + +extern void startmodelbatches(); +extern void endmodelbatches(); +extern void rendermodel(entitylight *light, const char *mdl, int anim, const vec &o, float yaw = 0, float pitch = 0, int cull = MDL_CULL_VFC | MDL_CULL_DIST | MDL_CULL_OCCLUDED | MDL_LIGHT, dynent *d = NULL, modelattach *a = NULL, int basetime = 0, int basetime2 = 0, float trans = 1); +extern void abovemodel(vec &o, const char *mdl); +extern void rendershadow(dynent *d); +extern void renderclient(dynent *d, const char *mdlname, modelattach *attachments, int hold, int attack, int attackdelay, int lastaction, int lastpain, float fade = 1, bool ragdoll = false); +extern void interpolateorientation(dynent *d, float &interpyaw, float &interppitch); +extern void setbbfrommodel(dynent *d, const char *mdl); +extern const char *mapmodelname(int i); +extern model *loadmodel(const char *name, int i = -1, bool msg = false); +extern void preloadmodel(const char *name); +extern void flushpreloadedmodels(bool msg = true); + +// ragdoll + +extern void moveragdoll(dynent *d); +extern void cleanragdoll(dynent *d); + +// server +#define MAXCLIENTS 128 // DO NOT set this any higher +#define MAXTRANS 5000 // max amount of data to swallow in 1 go + +extern int maxclients; + +enum { DISC_NONE = 0, DISC_EOP, DISC_LOCAL, DISC_KICK, DISC_MSGERR, DISC_IPBAN, DISC_PRIVATE, DISC_MAXCLIENTS, DISC_TIMEOUT, DISC_OVERFLOW, DISC_PASSWORD, DISC_NUM }; + +extern void *getclientinfo(int i); +extern ENetPeer *getclientpeer(int i); +extern ENetPacket *sendf(int cn, int chan, const char *format, ...); +extern ENetPacket *sendfile(int cn, int chan, stream *file, const char *format = "", ...); +extern void sendpacket(int cn, int chan, ENetPacket *packet, int exclude = -1); +extern void flushserver(bool force); +extern int getservermtu(); +extern int getnumclients(); +extern uint getclientip(int n); +extern void localconnect(); +extern const char *disconnectreason(int reason); +extern void disconnect_client(int n, int reason); +extern void kicknonlocalclients(int reason = DISC_NONE); +extern bool hasnonlocalclients(); +extern bool haslocalclients(); +extern void sendserverinforeply(ucharbuf &p); +extern bool requestmaster(const char *req); +extern bool requestmasterf(const char *fmt, ...) PRINTFARGS(1, 2); +extern bool isdedicatedserver(); + +// client +extern void sendclientpacket(ENetPacket *packet, int chan); +extern void flushclient(); +extern void disconnect(bool async = false, bool cleanup = true); +extern bool isconnected(bool attempt = false, bool local = true); +extern const ENetAddress *connectedpeer(); +extern bool multiplayer(bool msg = true); +extern void neterr(const char *s, bool disc = true); +extern void gets2c(); +extern void notifywelcome(); + +// crypto +extern void genprivkey(const char *seed, vector<char> &privstr, vector<char> &pubstr); +extern bool calcpubkey(const char *privstr, vector<char> &pubstr); +extern bool hashstring(const char *str, char *result, int maxlen); +extern void answerchallenge(const char *privstr, const char *challenge, vector<char> &answerstr); +extern void *parsepubkey(const char *pubstr); +extern void freepubkey(void *pubkey); +extern void *genchallenge(void *pubkey, const void *seed, int seedlen, vector<char> &challengestr); +extern void freechallenge(void *answer); +extern bool checkchallenge(const char *answerstr, void *correct); + +// 3dgui +struct Texture; +struct VSlot; + +enum { G3D_DOWN = 1, G3D_UP = 2, G3D_PRESSED = 4, G3D_ROLLOVER = 8, G3D_DRAGGED = 16 }; + +enum { EDITORFOCUSED = 1, EDITORUSED, EDITORFOREVER }; + +struct g3d_gui +{ + virtual ~g3d_gui() {} + + virtual void start(int starttime, float basescale, int *tab = NULL, bool allowinput = true) = 0; + virtual void end() = 0; + + virtual int text(const char *text, int color, const char *icon = NULL) = 0; + int textf(const char *fmt, int color, const char *icon = NULL, ...) PRINTFARGS(2, 5) + { + defvformatstring(str, icon, fmt); + return text(str, color, icon); + } + virtual int button(const char *text, int color, const char *icon = NULL) = 0; + int buttonf(const char *fmt, int color, const char *icon = NULL, ...) PRINTFARGS(2, 5) + { + defvformatstring(str, icon, fmt); + return button(str, color, icon); + } + virtual int title(const char *text, int color, const char *icon = NULL) = 0; + int titlef(const char *fmt, int color, const char *icon = NULL, ...) PRINTFARGS(2, 5) + { + defvformatstring(str, icon, fmt); + return title(str, color, icon); + } + virtual void background(int color, int parentw = 0, int parenth = 0) = 0; + + virtual void pushlist() {} + virtual void poplist() {} + + virtual bool allowautotab(bool on) = 0; + virtual bool shouldtab() { return false; } + virtual void tab(const char *name = NULL, int color = 0) = 0; + virtual int image(Texture *t, float scale, const char *overlaid = NULL) = 0; + virtual int texture(VSlot &vslot, float scale, bool overlaid = true) = 0; + virtual int playerpreview(int model, int team, int weap, float scale, const char *overlaid = NULL) { return 0; } + virtual int modelpreview(const char *name, int anim, float scale, const char *overlaid = NULL, bool throttle = false) { return 0; } + virtual int prefabpreview(const char *prefab, const vec &color, float scale, const char *overlaid = NULL, bool throttle = false) { return 0; } + virtual void slider(int &val, int vmin, int vmax, int color, const char *label = NULL) = 0; + virtual void separator() = 0; + virtual void progress(float percent) = 0; + virtual void strut(float size) = 0; + virtual void space(float size) = 0; + virtual void spring(int weight = 1) = 0; + virtual void column(int col) = 0; + virtual char *keyfield(const char *name, int color, int length, int height = 0, const char *initval = NULL, int initmode = EDITORFOCUSED) = 0; + virtual char *field(const char *name, int color, int length, int height = 0, const char *initval = NULL, int initmode = EDITORFOCUSED) = 0; + virtual void textbox(const char *text, int width, int height, int color = 0xFFFFFF) = 0; + virtual bool mergehits(bool on) = 0; +}; + +struct g3d_callback +{ + virtual ~g3d_callback() {} + + int starttime() { return totalmillis; } + + virtual void gui(g3d_gui &g, bool firstpass) = 0; +}; + +enum +{ + GUI_2D = 1<<0, + GUI_FOLLOW = 1<<1, + GUI_FORCE_2D = 1<<2, + GUI_BOTTOM = 1<<3 +}; + +extern void g3d_addgui(g3d_callback *cb, vec &origin, int flags = 0); +extern bool g3d_movecursor(int dx, int dy); +extern void g3d_cursorpos(float &x, float &y); +extern void g3d_resetcursor(); +extern void g3d_limitscale(float scale); + diff --git a/src/shared/igame.h b/src/shared/igame.h new file mode 100644 index 0000000..113ca17 --- /dev/null +++ b/src/shared/igame.h @@ -0,0 +1,130 @@ +// the interface the engine uses to run the gameplay module + +namespace entities +{ + extern void editent(int i, bool local); + extern const char *entnameinfo(entity &e); + extern const char *entname(int i); + extern int extraentinfosize(); + extern void writeent(entity &e, char *buf); + extern void readent(entity &e, char *buf, int ver); + extern float dropheight(entity &e); + extern void fixentity(extentity &e); + extern void entradius(extentity &e, bool color); + extern bool mayattach(extentity &e); + extern bool attachent(extentity &e, extentity &a); + extern bool printent(extentity &e, char *buf, int len); + extern extentity *newentity(); + extern void deleteentity(extentity *e); + extern void clearents(); + extern vector<extentity *> &getents(); + extern const char *entmodel(const entity &e); + extern void animatemapmodel(const extentity &e, int &anim, int &basetime); +} + +namespace game +{ + extern void parseoptions(vector<const char *> &args); + + extern void gamedisconnect(bool cleanup); + extern void parsepacketclient(int chan, packetbuf &p); + extern void connectattempt(const char *name, const char *password, const ENetAddress &address); + extern void connectfail(); + extern void gameconnect(bool _remote); + extern bool allowedittoggle(); + extern void edittoggled(bool on); + extern void writeclientinfo(stream *f); + extern void toserver(char *text); + extern void changemap(const char *name); + extern void forceedit(const char *name); + extern bool ispaused(); + extern int scaletime(int t); + extern bool allowmouselook(); + + extern const char *gameident(); + extern const char *savedconfig(); + extern const char *restoreconfig(); + extern const char *defaultconfig(); + extern const char *autoexec(); + extern const char *savedservers(); + extern void loadconfigs(); + + extern void updateworld(); + extern void initclient(); + extern void physicstrigger(physent *d, bool local, int floorlevel, int waterlevel, int material = 0); + extern void bounced(physent *d, const vec &surface); + extern void edittrigger(const selinfo &sel, int op, int arg1 = 0, int arg2 = 0, int arg3 = 0, const VSlot *vs = NULL); + extern void vartrigger(ident *id); + extern void dynentcollide(physent *d, physent *o, const vec &dir); + extern const char *getclientmap(); + extern const char *getmapinfo(); + extern const char *getscreenshotinfo(); + extern void resetgamestate(); + extern void suicide(physent *d); + extern float ratespawn(dynent *d, const extentity &e); + extern void newmap(int size); + extern void loadingmap(const char *name); + extern void startmap(const char *name); + extern void preload(); + extern float abovegameplayhud(int w, int h); + extern void gameplayhud(int w, int h); + extern bool canjump(); + extern bool allowmove(physent *d); + extern void doattack(bool on); + extern dynent *iterdynents(int i); + extern int numdynents(); + extern void rendergame(bool mainpass); + extern void renderavatar(); + extern void renderplayerpreview(int model, int team, int weap); + extern void writegamedata(vector<char> &extras); + extern void readgamedata(vector<char> &extras); + extern int clipconsole(int w, int h); + extern void g3d_gamemenus(); + extern const char *defaultcrosshair(int index); + extern int selectcrosshair(vec &color); + extern void lighteffects(dynent *d, vec &color, vec &dir); + extern void setupcamera(); + extern bool allowthirdperson(bool msg = false); + extern bool detachcamera(); + extern bool collidecamera(); + extern void adddynlights(); + extern void particletrack(physent *owner, vec &o, vec &d); + extern void dynlighttrack(physent *owner, vec &o, vec &hud); + extern int maxsoundradius(int n); + extern bool serverinfostartcolumn(g3d_gui *g, int i); + extern void serverinfoendcolumn(g3d_gui *g, int i); + extern bool serverinfoentry(g3d_gui *g, int i, const char *name, int port, const char *desc, const char *map, int ping, const vector<int> &attr, int np); + extern bool needminimap(); +} + +namespace server +{ + extern void *newclientinfo(); + extern void deleteclientinfo(void *ci); + extern void serverinit(); + extern int reserveclients(); + extern int numchannels(); + extern void clientdisconnect(int n); + extern int clientconnect(int n, uint ip); + extern void localdisconnect(int n); + extern void localconnect(int n); + extern bool allowbroadcast(int n); + extern void recordpacket(int chan, void *data, int len); + extern void parsepacket(int sender, int chan, packetbuf &p); + extern void sendservmsg(const char *s); + extern bool sendpackets(bool force = false); + extern void serverinforeply(ucharbuf &req, ucharbuf &p); + extern void serverupdate(); + extern bool servercompatible(char *name, char *sdec, char *map, int ping, const vector<int> &attr, int np); + extern int laninfoport(); + extern int serverinfoport(int servport = -1); + extern int serverport(int infoport = -1); + extern const char *defaultmaster(); + extern int masterport(); + extern void processmasterinput(const char *cmd, int cmdlen, const char *args); + extern void masterconnected(); + extern void masterdisconnected(); + extern bool ispaused(); + extern int scaletime(int t); +} + diff --git a/src/shared/stream.cpp b/src/shared/stream.cpp new file mode 100644 index 0000000..f2b586e --- /dev/null +++ b/src/shared/stream.cpp @@ -0,0 +1,1264 @@ +#include "cube.h" + +///////////////////////////// console //////////////////////// + +void conoutf(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + conoutfv(CON_INFO, fmt, args); + va_end(args); +} + +void conoutf(int type, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + conoutfv(type, fmt, args); + va_end(args); +} + +void conoutf(int type, int tag, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + conoutfv(type | ((tag << CON_TAG_SHIFT) & CON_TAG_MASK), fmt, args); + va_end(args); +} + +///////////////////////// character conversion /////////////// + +#define CUBECTYPE(s, p, d, a, A, u, U) \ + 0, U, U, U, U, U, U, U, U, s, s, s, s, s, U, U, \ + U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, \ + s, p, p, p, p, p, p, p, p, p, p, p, p, p, p, p, \ + d, d, d, d, d, d, d, d, d, d, p, p, p, p, p, p, \ + p, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, \ + A, A, A, A, A, A, A, A, A, A, A, p, p, p, p, p, \ + p, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, \ + a, a, a, a, a, a, a, a, a, a, a, p, p, p, p, U, \ + U, u, u, u, u, u, u, u, u, u, u, u, u, u, u, u, \ + u, u, u, u, u, u, u, u, u, u, u, u, u, u, u, U, \ + u, U, u, U, u, U, u, U, u, U, u, U, u, U, u, U, \ + u, U, u, U, u, U, u, U, u, U, u, U, u, U, u, U, \ + u, U, u, U, u, U, u, U, U, u, U, u, U, u, U, U, \ + U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, \ + U, U, U, U, u, u, u, u, u, u, u, u, u, u, u, u, \ + u, u, u, u, u, u, u, u, u, u, u, u, u, u, U, u + +extern const uchar cubectype[256] = +{ + CUBECTYPE(CT_SPACE, + CT_PRINT, + CT_PRINT|CT_DIGIT, + CT_PRINT|CT_ALPHA|CT_LOWER, + CT_PRINT|CT_ALPHA|CT_UPPER, + CT_PRINT|CT_UNICODE|CT_ALPHA|CT_LOWER, + CT_PRINT|CT_UNICODE|CT_ALPHA|CT_UPPER) +}; +extern const int cube2unichars[256] = +{ + 0, 192, 193, 194, 195, 196, 197, 198, 199, 9, 10, 11, 12, 13, 200, 201, + 202, 203, 204, 205, 206, 207, 209, 210, 211, 212, 213, 214, 216, 217, 218, 219, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 220, + 221, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, + 238, 239, 241, 242, 243, 244, 245, 246, 248, 249, 250, 251, 252, 253, 255, 0x104, + 0x105, 0x106, 0x107, 0x10C, 0x10D, 0x10E, 0x10F, 0x118, 0x119, 0x11A, 0x11B, 0x11E, 0x11F, 0x130, 0x131, 0x141, + 0x142, 0x143, 0x144, 0x147, 0x148, 0x150, 0x151, 0x152, 0x153, 0x158, 0x159, 0x15A, 0x15B, 0x15E, 0x15F, 0x160, + 0x161, 0x164, 0x165, 0x16E, 0x16F, 0x170, 0x171, 0x178, 0x179, 0x17A, 0x17B, 0x17C, 0x17D, 0x17E, 0x404, 0x411, + 0x413, 0x414, 0x416, 0x417, 0x418, 0x419, 0x41B, 0x41F, 0x423, 0x424, 0x426, 0x427, 0x428, 0x429, 0x42A, 0x42B, + 0x42C, 0x42D, 0x42E, 0x42F, 0x431, 0x432, 0x433, 0x434, 0x436, 0x437, 0x438, 0x439, 0x43A, 0x43B, 0x43C, 0x43D, + 0x43F, 0x442, 0x444, 0x446, 0x447, 0x448, 0x449, 0x44A, 0x44B, 0x44C, 0x44D, 0x44E, 0x44F, 0x454, 0x490, 0x491 +}; +extern const int uni2cubeoffsets[8] = +{ + 0, 256, 658, 658, 512, 658, 658, 658 +}; +extern const uchar uni2cubechars[878] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 10, 11, 12, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 2, 3, 4, 5, 6, 7, 8, 14, 15, 16, 17, 18, 19, 20, 21, 0, 22, 23, 24, 25, 26, 27, 0, 28, 29, 30, 31, 127, 128, 0, 129, + 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 0, 146, 147, 148, 149, 150, 151, 0, 152, 153, 154, 155, 156, 157, 0, 158, + 0, 0, 0, 0, 159, 160, 161, 162, 0, 0, 0, 0, 163, 164, 165, 166, 0, 0, 0, 0, 0, 0, 0, 0, 167, 168, 169, 170, 0, 0, 171, 172, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 173, 174, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 175, 176, 177, 178, 0, 0, 179, 180, 0, 0, 0, 0, 0, 0, 0, 181, 182, 183, 184, 0, 0, 0, 0, 185, 186, 187, 188, 0, 0, 189, 190, + 191, 192, 0, 0, 193, 194, 0, 0, 0, 0, 0, 0, 0, 0, 195, 196, 197, 198, 0, 0, 0, 0, 0, 0, 199, 200, 201, 202, 203, 204, 205, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 17, 0, 0, 206, 83, 73, 21, 74, 0, 0, 0, 0, 0, 0, 0, 65, 207, 66, 208, 209, 69, 210, 211, 212, 213, 75, 214, 77, 72, 79, 215, + 80, 67, 84, 216, 217, 88, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 97, 228, 229, 230, 231, 101, 232, 233, 234, 235, 236, 237, 238, 239, 111, 240, + 112, 99, 241, 121, 242, 120, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 0, 141, 0, 0, 253, 115, 105, 145, 106, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 254, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; +extern const uchar cubelowerchars[256] = +{ + 0, 130, 131, 132, 133, 134, 135, 136, 137, 9, 10, 11, 12, 13, 138, 139, + 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 156, + 157, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, + 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 160, + 160, 162, 162, 164, 164, 166, 166, 168, 168, 170, 170, 172, 172, 105, 174, 176, + 176, 178, 178, 180, 180, 182, 182, 184, 184, 186, 186, 188, 188, 190, 190, 192, + 192, 194, 194, 196, 196, 198, 198, 158, 201, 201, 203, 203, 205, 205, 206, 207, + 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, + 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 +}; +extern const uchar cubeupperchars[256] = +{ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 96, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 123, 124, 125, 126, 127, + 128, 129, 1, 2, 3, 4, 5, 6, 7, 8, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 127, 128, 199, 159, + 159, 161, 161, 163, 163, 165, 165, 167, 167, 169, 169, 171, 171, 173, 73, 175, + 175, 177, 177, 179, 179, 181, 181, 183, 183, 185, 185, 187, 187, 189, 189, 191, + 191, 193, 193, 195, 195, 197, 197, 199, 200, 200, 202, 202, 204, 204, 206, 207, + 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, + 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 +}; + +size_t decodeutf8(uchar *dstbuf, size_t dstlen, const uchar *srcbuf, size_t srclen, size_t *carry) +{ + uchar *dst = dstbuf, *dstend = &dstbuf[dstlen]; + const uchar *src = srcbuf, *srcend = &srcbuf[srclen]; + if(dstbuf == srcbuf) + { + int len = min(dstlen, srclen); + for(const uchar *end4 = &srcbuf[len&~3]; src < end4; src += 4) if(*(const int *)src & 0x80808080) goto decode; + for(const uchar *end = &srcbuf[len]; src < end; src++) if(*src & 0x80) goto decode; + if(carry) *carry += len; + return len; + } + +decode: + dst += src - srcbuf; + while(src < srcend && dst < dstend) + { + int c = *src++; + if(c < 0x80) *dst++ = c; + else if(c >= 0xC0) + { + int uni; + if(c >= 0xE0) + { + if(c >= 0xF0) + { + if(c >= 0xF8) + { + if(c >= 0xFC) + { + if(c >= 0xFE) continue; + uni = c&1; if(srcend - src < 5) break; + c = *src; if((c&0xC0) != 0x80) continue; src++; uni = (uni<<6) | (c&0x3F); + } + else { uni = c&3; if(srcend - src < 4) break; } + c = *src; if((c&0xC0) != 0x80) continue; src++; uni = (uni<<6) | (c&0x3F); + } + else { uni = c&7; if(srcend - src < 3) break; } + c = *src; if((c&0xC0) != 0x80) continue; src++; uni = (uni<<6) | (c&0x3F); + } + else { uni = c&0xF; if(srcend - src < 2) break; } + c = *src; if((c&0xC0) != 0x80) continue; src++; uni = (uni<<6) | (c&0x3F); + } + else { uni = c&0x1F; if(srcend - src < 1) break; } + c = *src; if((c&0xC0) != 0x80) continue; src++; uni = (uni<<6) | (c&0x3F); + c = uni2cube(uni); + if(!c) continue; + *dst++ = c; + } + } + if(carry) *carry += src - srcbuf; + return dst - dstbuf; +} + +size_t encodeutf8(uchar *dstbuf, size_t dstlen, const uchar *srcbuf, size_t srclen, size_t *carry) +{ + uchar *dst = dstbuf, *dstend = &dstbuf[dstlen]; + const uchar *src = srcbuf, *srcend = &srcbuf[srclen]; + if(src < srcend && dst < dstend) do + { + int uni = cube2uni(*src); + if(uni <= 0x7F) + { + if(dst >= dstend) goto done; + const uchar *end = min(srcend, &src[dstend-dst]); + do + { + if(uni == '\f') + { + if(++src >= srcend) goto done; + goto uni1; + } + *dst++ = uni; + if(++src >= end) goto done; + uni = cube2uni(*src); + } + while(uni <= 0x7F); + } + if(uni <= 0x7FF) { if(dst + 2 > dstend) goto done; *dst++ = 0xC0 | (uni>>6); goto uni2; } + else if(uni <= 0xFFFF) { if(dst + 3 > dstend) goto done; *dst++ = 0xE0 | (uni>>12); goto uni3; } + else if(uni <= 0x1FFFFF) { if(dst + 4 > dstend) goto done; *dst++ = 0xF0 | (uni>>18); goto uni4; } + else if(uni <= 0x3FFFFFF) { if(dst + 5 > dstend) goto done; *dst++ = 0xF8 | (uni>>24); goto uni5; } + else if(uni <= 0x7FFFFFFF) { if(dst + 6 > dstend) goto done; *dst++ = 0xFC | (uni>>30); goto uni6; } + else goto uni1; + uni6: *dst++ = 0x80 | ((uni>>24)&0x3F); + uni5: *dst++ = 0x80 | ((uni>>18)&0x3F); + uni4: *dst++ = 0x80 | ((uni>>12)&0x3F); + uni3: *dst++ = 0x80 | ((uni>>6)&0x3F); + uni2: *dst++ = 0x80 | (uni&0x3F); + uni1:; + } + while(++src < srcend); + +done: + if(carry) *carry += src - srcbuf; + return dst - dstbuf; +} + +///////////////////////// file system /////////////////////// + +#ifdef WIN32 +#include <shlobj.h> +#else +#include <unistd.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <dirent.h> +#endif + +string homedir = ""; +struct packagedir +{ + char *dir, *filter; + size_t dirlen, filterlen; +}; +vector<packagedir> packagedirs; + +char *makerelpath(const char *dir, const char *file, const char *prefix, const char *cmd) +{ + static string tmp; + if(prefix) copystring(tmp, prefix); + else tmp[0] = '\0'; + if(file[0]=='<') + { + const char *end = strrchr(file, '>'); + if(end) + { + size_t len = strlen(tmp); + copystring(&tmp[len], file, min(sizeof(tmp)-len, size_t(end+2-file))); + file = end+1; + } + } + if(cmd) concatstring(tmp, cmd); + if(dir) + { + defformatstring(pname, "%s/%s", dir, file); + concatstring(tmp, pname); + } + else concatstring(tmp, file); + return tmp; +} + + +char *path(char *s) +{ + for(char *curpart = s;;) + { + char *endpart = strchr(curpart, '&'); + if(endpart) *endpart = '\0'; + if(curpart[0]=='<') + { + char *file = strrchr(curpart, '>'); + if(!file) return s; + curpart = file+1; + } + for(char *t = curpart; (t = strpbrk(t, "/\\")); *t++ = PATHDIV); + for(char *prevdir = NULL, *curdir = curpart;;) + { + prevdir = curdir[0]==PATHDIV ? curdir+1 : curdir; + curdir = strchr(prevdir, PATHDIV); + if(!curdir) break; + if(prevdir+1==curdir && prevdir[0]=='.') + { + memmove(prevdir, curdir+1, strlen(curdir+1)+1); + curdir = prevdir; + } + else if(curdir[1]=='.' && curdir[2]=='.' && curdir[3]==PATHDIV) + { + if(prevdir+2==curdir && prevdir[0]=='.' && prevdir[1]=='.') continue; + memmove(prevdir, curdir+4, strlen(curdir+4)+1); + if(prevdir-2 >= curpart && prevdir[-1]==PATHDIV) + { + prevdir -= 2; + while(prevdir-1 >= curpart && prevdir[-1] != PATHDIV) --prevdir; + } + curdir = prevdir; + } + } + if(endpart) + { + *endpart = '&'; + curpart = endpart+1; + } + else break; + } + return s; +} + +char *path(const char *s, bool copy) +{ + static string tmp; + copystring(tmp, s); + path(tmp); + return tmp; +} + +const char *parentdir(const char *directory) +{ + const char *p = directory + strlen(directory); + while(p > directory && *p != '/' && *p != '\\') p--; + static string parent; + size_t len = p-directory+1; + copystring(parent, directory, len); + return parent; +} + +bool fileexists(const char *path, const char *mode) +{ + bool exists = true; + if(mode[0]=='w' || mode[0]=='a') path = parentdir(path); +#ifdef WIN32 + if(GetFileAttributes(path[0] ? path : ".\\") == INVALID_FILE_ATTRIBUTES) exists = false; +#else + if(access(path[0] ? path : ".", mode[0]=='w' || mode[0]=='a' ? W_OK : (mode[0]=='d' ? X_OK : R_OK)) == -1) exists = false; +#endif + return exists; +} + +bool createdir(const char *path) +{ + size_t len = strlen(path); + if(path[len-1]==PATHDIV) + { + static string strip; + path = copystring(strip, path, len); + } +#ifdef WIN32 + return CreateDirectory(path, NULL)!=0; +#else + return mkdir(path, 0777)==0; +#endif +} + +size_t fixpackagedir(char *dir) +{ + path(dir); + size_t len = strlen(dir); + if(len > 0 && dir[len-1] != PATHDIV) + { + dir[len] = PATHDIV; + dir[len+1] = '\0'; + } + return len; +} + +bool subhomedir(char *dst, int len, const char *src) +{ + const char *sub = strstr(src, "$HOME"); + if(!sub) sub = strchr(src, '~'); + if(sub && sub-src < len) + { +#ifdef WIN32 + char home[MAX_PATH+1]; + home[0] = '\0'; + if(SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, home) != S_OK || !home[0]) return false; +#else + const char *home = getenv("HOME"); + if(!home || !home[0]) return false; +#endif + dst[sub-src] = '\0'; + concatstring(dst, home, len); + concatstring(dst, sub+(*sub == '~' ? 1 : strlen("$HOME")), len); + } + return true; +} + +const char *sethomedir(const char *dir) +{ + string pdir; + copystring(pdir, dir); + if(!subhomedir(pdir, sizeof(pdir), dir) || !fixpackagedir(pdir)) return NULL; + copystring(homedir, pdir); + return homedir; +} + +const char *addpackagedir(const char *dir) +{ + string pdir; + copystring(pdir, dir); + if(!subhomedir(pdir, sizeof(pdir), dir) || !fixpackagedir(pdir)) return NULL; + char *filter = pdir; + for(;;) + { + static int len = strlen("packages"); + filter = strstr(filter, "packages"); + if(!filter) break; + if(filter > pdir && filter[-1] == PATHDIV && filter[len] == PATHDIV) break; + filter += len; + } + packagedir &pf = packagedirs.add(); + pf.dir = filter ? newstring(pdir, filter-pdir) : newstring(pdir); + pf.dirlen = filter ? filter-pdir : strlen(pdir); + pf.filter = filter ? newstring(filter) : NULL; + pf.filterlen = filter ? strlen(filter) : 0; + return pf.dir; +} + +const char *findfile(const char *filename, const char *mode) +{ + static string s; + if(homedir[0]) + { + formatstring(s, "%s%s", homedir, filename); + if(fileexists(s, mode)) return s; + if(mode[0]=='w' || mode[0]=='a') + { + string dirs; + copystring(dirs, s); + char *dir = strchr(dirs[0]==PATHDIV ? dirs+1 : dirs, PATHDIV); + while(dir) + { + *dir = '\0'; + if(!fileexists(dirs, "d") && !createdir(dirs)) return s; + *dir = PATHDIV; + dir = strchr(dir+1, PATHDIV); + } + return s; + } + } + if(mode[0]=='w' || mode[0]=='a') return filename; + loopv(packagedirs) + { + packagedir &pf = packagedirs[i]; + if(pf.filter && strncmp(filename, pf.filter, pf.filterlen)) continue; + formatstring(s, "%s%s", pf.dir, filename); + if(fileexists(s, mode)) return s; + } + if(mode[0]=='e') return NULL; + return filename; +} + +bool listdir(const char *dirname, bool rel, const char *ext, vector<char *> &files) +{ + size_t extsize = ext ? strlen(ext)+1 : 0; +#ifdef WIN32 + defformatstring(pathname, rel ? ".\\%s\\*.%s" : "%s\\*.%s", dirname, ext ? ext : "*"); + WIN32_FIND_DATA FindFileData; + HANDLE Find = FindFirstFile(pathname, &FindFileData); + if(Find != INVALID_HANDLE_VALUE) + { + do { + if(!ext) files.add(newstring(FindFileData.cFileName)); + else + { + size_t namelen = strlen(FindFileData.cFileName); + if(namelen > extsize) + { + namelen -= extsize; + if(FindFileData.cFileName[namelen] == '.' && strncmp(FindFileData.cFileName+namelen+1, ext, extsize-1)==0) + files.add(newstring(FindFileData.cFileName, namelen)); + } + } + } while(FindNextFile(Find, &FindFileData)); + FindClose(Find); + return true; + } +#else + defformatstring(pathname, rel ? "./%s" : "%s", dirname); + DIR *d = opendir(pathname); + if(d) + { + struct dirent *de; + while((de = readdir(d)) != NULL) + { + if(!ext) files.add(newstring(de->d_name)); + else + { + size_t namelen = strlen(de->d_name); + if(namelen > extsize) + { + namelen -= extsize; + if(de->d_name[namelen] == '.' && strncmp(de->d_name+namelen+1, ext, extsize-1)==0) + files.add(newstring(de->d_name, namelen)); + } + } + } + closedir(d); + return true; + } +#endif + else return false; +} + +int listfiles(const char *dir, const char *ext, vector<char *> &files) +{ + string dirname; + copystring(dirname, dir); + path(dirname); + size_t dirlen = strlen(dirname); + while(dirlen > 1 && dirname[dirlen-1] == PATHDIV) dirname[--dirlen] = '\0'; + int dirs = 0; + if(listdir(dirname, true, ext, files)) dirs++; + string s; + if(homedir[0]) + { + formatstring(s, "%s%s", homedir, dirname); + if(listdir(s, false, ext, files)) dirs++; + } + loopv(packagedirs) + { + packagedir &pf = packagedirs[i]; + if(pf.filter && strncmp(dirname, pf.filter, dirlen == pf.filterlen-1 ? dirlen : pf.filterlen)) + continue; + formatstring(s, "%s%s", pf.dir, dirname); + if(listdir(s, false, ext, files)) dirs++; + } +#ifndef STANDALONE + dirs += listzipfiles(dirname, ext, files); +#endif + return dirs; +} + +#ifndef STANDALONE +static Sint64 rwopsseek(SDL_RWops *rw, Sint64 pos, int whence) +{ + stream *f = (stream *)rw->hidden.unknown.data1; + if((!pos && whence==SEEK_CUR) || f->seek(pos, whence)) return (int)f->tell(); + return -1; +} + +static size_t rwopsread(SDL_RWops *rw, void *buf, size_t size, size_t nmemb) +{ + stream *f = (stream *)rw->hidden.unknown.data1; + return f->read(buf, size*nmemb)/size; +} + +static size_t rwopswrite(SDL_RWops *rw, const void *buf, size_t size, size_t nmemb) +{ + stream *f = (stream *)rw->hidden.unknown.data1; + return f->write(buf, size*nmemb)/size; +} + +static int rwopsclose(SDL_RWops *rw) +{ + return 0; +} + +SDL_RWops *stream::rwops() +{ + SDL_RWops *rw = SDL_AllocRW(); + if(!rw) return NULL; + rw->hidden.unknown.data1 = this; + rw->seek = rwopsseek; + rw->read = rwopsread; + rw->write = rwopswrite; + rw->close = rwopsclose; + return rw; +} +#endif + +stream::offset stream::size() +{ + offset pos = tell(), endpos; + if(pos < 0 || !seek(0, SEEK_END)) return -1; + endpos = tell(); + return pos == endpos || seek(pos, SEEK_SET) ? endpos : -1; +} + +bool stream::getline(char *str, size_t len) +{ + loopi(len-1) + { + if(read(&str[i], 1) != 1) { str[i] = '\0'; return i > 0; } + else if(str[i] == '\n') { str[i+1] = '\0'; return true; } + } + if(len > 0) str[len-1] = '\0'; + return true; +} + +size_t stream::printf(const char *fmt, ...) +{ + char buf[512]; + char *str = buf; + va_list args; +#if defined(WIN32) && !defined(__GNUC__) + va_start(args, fmt); + int len = _vscprintf(fmt, args); + if(len <= 0) { va_end(args); return 0; } + if(len >= (int)sizeof(buf)) str = new char[len+1]; + _vsnprintf(str, len+1, fmt, args); + va_end(args); +#else + va_start(args, fmt); + int len = vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + if(len <= 0) return 0; + if(len >= (int)sizeof(buf)) + { + str = new char[len+1]; + va_start(args, fmt); + vsnprintf(str, len+1, fmt, args); + va_end(args); + } +#endif + size_t n = write(str, len); + if(str != buf) delete[] str; + return n; +} + +struct filestream : stream +{ + FILE *file; + + filestream() : file(NULL) {} + ~filestream() { close(); } + + bool open(const char *name, const char *mode) + { + if(file) return false; + file = fopen(name, mode); + return file!=NULL; + } + + bool opentemp(const char *name, const char *mode) + { + if(file) return false; +#ifdef WIN32 + file = fopen(name, mode); +#else + file = tmpfile(); +#endif + return file!=NULL; + } + + void close() + { + if(file) { fclose(file); file = NULL; } + } + + bool end() { return feof(file)!=0; } + offset tell() + { +#ifdef WIN32 +#if defined(__GNUC__) && !defined(__MINGW32__) + offset off = ftello64(file); +#else + offset off = _ftelli64(file); +#endif +#else + offset off = ftello(file); +#endif + // ftello returns LONG_MAX for directories on some platforms + return off + 1 >= 0 ? off : -1; + } + bool seek(offset pos, int whence) + { +#ifdef WIN32 +#if defined(__GNUC__) && !defined(__MINGW32__) + return fseeko64(file, pos, whence) >= 0; +#else + return _fseeki64(file, pos, whence) >= 0; +#endif +#else + return fseeko(file, pos, whence) >= 0; +#endif + } + + size_t read(void *buf, size_t len) { return fread(buf, 1, len, file); } + size_t write(const void *buf, size_t len) { return fwrite(buf, 1, len, file); } + bool flush() { return !fflush(file); } + int getchar() { return fgetc(file); } + bool putchar(int c) { return fputc(c, file)!=EOF; } + bool getline(char *str, size_t len) { return fgets(str, len, file)!=NULL; } + bool putstring(const char *str) { return fputs(str, file)!=EOF; } + + size_t printf(const char *fmt, ...) + { + va_list v; + va_start(v, fmt); + int result = vfprintf(file, fmt, v); + va_end(v); + return max(result, 0); + } +}; + +#ifndef STANDALONE +VAR(dbggz, 0, 0, 1); +#endif + +struct gzstream : stream +{ + enum + { + MAGIC1 = 0x1F, + MAGIC2 = 0x8B, + BUFSIZE = 16384, + OS_UNIX = 0x03 + }; + + enum + { + F_ASCII = 0x01, + F_CRC = 0x02, + F_EXTRA = 0x04, + F_NAME = 0x08, + F_COMMENT = 0x10, + F_RESERVED = 0xE0 + }; + + stream *file; + z_stream zfile; + uchar *buf; + bool reading, writing, autoclose; + uint crc; + size_t headersize; + + gzstream() : file(NULL), buf(NULL), reading(false), writing(false), autoclose(false), crc(0), headersize(0) + { + zfile.zalloc = NULL; + zfile.zfree = NULL; + zfile.opaque = NULL; + zfile.next_in = zfile.next_out = NULL; + zfile.avail_in = zfile.avail_out = 0; + } + + ~gzstream() + { + close(); + } + + void writeheader() + { + uchar header[] = { MAGIC1, MAGIC2, Z_DEFLATED, 0, 0, 0, 0, 0, 0, OS_UNIX }; + file->write(header, sizeof(header)); + } + + void readbuf(size_t size = BUFSIZE) + { + if(!zfile.avail_in) zfile.next_in = (Bytef *)buf; + size = min(size, size_t(&buf[BUFSIZE] - &zfile.next_in[zfile.avail_in])); + size_t n = file->read(zfile.next_in + zfile.avail_in, size); + if(n > 0) zfile.avail_in += n; + } + + uchar readbyte(size_t size = BUFSIZE) + { + if(!zfile.avail_in) readbuf(size); + if(!zfile.avail_in) return 0; + zfile.avail_in--; + return *(uchar *)zfile.next_in++; + } + + void skipbytes(size_t n) + { + while(n > 0 && zfile.avail_in > 0) + { + size_t skipped = min(n, size_t(zfile.avail_in)); + zfile.avail_in -= skipped; + zfile.next_in += skipped; + n -= skipped; + } + if(n <= 0) return; + file->seek(n, SEEK_CUR); + } + + bool checkheader() + { + readbuf(10); + if(readbyte() != MAGIC1 || readbyte() != MAGIC2 || readbyte() != Z_DEFLATED) return false; + uchar flags = readbyte(); + if(flags & F_RESERVED) return false; + skipbytes(6); + if(flags & F_EXTRA) + { + size_t len = readbyte(512); + len |= size_t(readbyte(512))<<8; + skipbytes(len); + } + if(flags & F_NAME) while(readbyte(512)); + if(flags & F_COMMENT) while(readbyte(512)); + if(flags & F_CRC) skipbytes(2); + headersize = size_t(file->tell() - zfile.avail_in); + return zfile.avail_in > 0 || !file->end(); + } + + bool open(stream *f, const char *mode, bool needclose, int level) + { + if(file) return false; + for(; *mode; mode++) + { + if(*mode=='r') { reading = true; break; } + else if(*mode=='w') { writing = true; break; } + } + if(reading) + { + if(inflateInit2(&zfile, -MAX_WBITS) != Z_OK) reading = false; + } + else if(writing && deflateInit2(&zfile, level, Z_DEFLATED, -MAX_WBITS, min(MAX_MEM_LEVEL, 8), Z_DEFAULT_STRATEGY) != Z_OK) writing = false; + if(!reading && !writing) return false; + + file = f; + crc = crc32(0, NULL, 0); + buf = new uchar[BUFSIZE]; + + if(reading) + { + if(!checkheader()) { stopreading(); return false; } + } + else if(writing) writeheader(); + + autoclose = needclose; + return true; + } + + uint getcrc() { return crc; } + + void finishreading() + { + if(!reading) return; +#ifndef STANDALONE + if(dbggz) + { + uint checkcrc = 0, checksize = 0; + loopi(4) checkcrc |= uint(readbyte()) << (i*8); + loopi(4) checksize |= uint(readbyte()) << (i*8); + if(checkcrc != crc) + conoutf(CON_DEBUG, "gzip crc check failed: read %X, calculated %X", checkcrc, crc); + if(checksize != zfile.total_out) + conoutf(CON_DEBUG, "gzip size check failed: read %u, calculated %u", checksize, uint(zfile.total_out)); + } +#endif + } + + void stopreading() + { + if(!reading) return; + inflateEnd(&zfile); + reading = false; + } + + void finishwriting() + { + if(!writing) return; + for(;;) + { + int err = zfile.avail_out > 0 ? deflate(&zfile, Z_FINISH) : Z_OK; + if(err != Z_OK && err != Z_STREAM_END) break; + flushbuf(); + if(err == Z_STREAM_END) break; + } + uchar trailer[8] = + { + uchar(crc&0xFF), uchar((crc>>8)&0xFF), uchar((crc>>16)&0xFF), uchar((crc>>24)&0xFF), + uchar(zfile.total_in&0xFF), uchar((zfile.total_in>>8)&0xFF), uchar((zfile.total_in>>16)&0xFF), uchar((zfile.total_in>>24)&0xFF) + }; + file->write(trailer, sizeof(trailer)); + } + + void stopwriting() + { + if(!writing) return; + deflateEnd(&zfile); + writing = false; + } + + void close() + { + if(reading) finishreading(); + stopreading(); + if(writing) finishwriting(); + stopwriting(); + DELETEA(buf); + if(autoclose) DELETEP(file); + } + + bool end() { return !reading && !writing; } + offset tell() { return reading ? zfile.total_out : (writing ? zfile.total_in : offset(-1)); } + offset rawtell() { return file ? file->tell() : offset(-1); } + + offset size() + { + if(!file) return -1; + offset pos = tell(); + if(!file->seek(-4, SEEK_END)) return -1; + uint isize = file->getlil<uint>(); + return file->seek(pos, SEEK_SET) ? isize : offset(-1); + } + + offset rawsize() { return file ? file->size() : offset(-1); } + + bool seek(offset pos, int whence) + { + if(writing || !reading) return false; + + if(whence == SEEK_END) + { + uchar skip[512]; + while(read(skip, sizeof(skip)) == sizeof(skip)); + return !pos; + } + else if(whence == SEEK_CUR) pos += zfile.total_out; + + if(pos >= (offset)zfile.total_out) pos -= zfile.total_out; + else if(pos < 0 || !file->seek(headersize, SEEK_SET)) return false; + else + { + if(zfile.next_in && zfile.total_in <= uint(zfile.next_in - buf)) + { + zfile.avail_in += zfile.total_in; + zfile.next_in -= zfile.total_in; + } + else + { + zfile.avail_in = 0; + zfile.next_in = NULL; + } + inflateReset(&zfile); + crc = crc32(0, NULL, 0); + } + + uchar skip[512]; + while(pos > 0) + { + size_t skipped = (size_t)min(pos, (offset)sizeof(skip)); + if(read(skip, skipped) != skipped) { stopreading(); return false; } + pos -= skipped; + } + + return true; + } + + size_t read(void *buf, size_t len) + { + if(!reading || !buf || !len) return 0; + zfile.next_out = (Bytef *)buf; + zfile.avail_out = len; + while(zfile.avail_out > 0) + { + if(!zfile.avail_in) + { + readbuf(BUFSIZE); + if(!zfile.avail_in) { stopreading(); break; } + } + int err = inflate(&zfile, Z_NO_FLUSH); + if(err == Z_STREAM_END) { crc = crc32(crc, (Bytef *)buf, len - zfile.avail_out); finishreading(); stopreading(); return len - zfile.avail_out; } + else if(err != Z_OK) { stopreading(); break; } + } + crc = crc32(crc, (Bytef *)buf, len - zfile.avail_out); + return len - zfile.avail_out; + } + + bool flushbuf(bool full = false) + { + if(full) deflate(&zfile, Z_SYNC_FLUSH); + if(zfile.next_out && zfile.avail_out < BUFSIZE) + { + if(file->write(buf, BUFSIZE - zfile.avail_out) != BUFSIZE - zfile.avail_out || (full && !file->flush())) + return false; + } + zfile.next_out = buf; + zfile.avail_out = BUFSIZE; + return true; + } + + bool flush() { return flushbuf(true); } + + size_t write(const void *buf, size_t len) + { + if(!writing || !buf || !len) return 0; + zfile.next_in = (Bytef *)buf; + zfile.avail_in = len; + while(zfile.avail_in > 0) + { + if(!zfile.avail_out && !flushbuf()) { stopwriting(); break; } + int err = deflate(&zfile, Z_NO_FLUSH); + if(err != Z_OK) { stopwriting(); break; } + } + crc = crc32(crc, (Bytef *)buf, len - zfile.avail_in); + return len - zfile.avail_in; + } +}; + +struct utf8stream : stream +{ + enum + { + BUFSIZE = 4096 + }; + stream *file; + offset pos; + size_t bufread, bufcarry, buflen; + bool reading, writing, autoclose; + uchar buf[BUFSIZE]; + + utf8stream() : file(NULL), pos(0), bufread(0), bufcarry(0), buflen(0), reading(false), writing(false), autoclose(false) + { + } + + ~utf8stream() + { + close(); + } + + bool readbuf(size_t size = BUFSIZE) + { + if(bufread >= bufcarry) { if(bufcarry > 0 && bufcarry < buflen) memmove(buf, &buf[bufcarry], buflen - bufcarry); buflen -= bufcarry; bufread = bufcarry = 0; } + size_t n = file->read(&buf[buflen], min(size, BUFSIZE - buflen)); + if(n <= 0) return false; + buflen += n; + size_t carry = bufcarry; + bufcarry += decodeutf8(&buf[bufcarry], BUFSIZE-bufcarry, &buf[bufcarry], buflen-bufcarry, &carry); + if(carry > bufcarry && carry < buflen) { memmove(&buf[bufcarry], &buf[carry], buflen - carry); buflen -= carry - bufcarry; } + return true; + } + + bool checkheader() + { + size_t n = file->read(buf, 3); + if(n == 3 && buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF) return true; + buflen = n; + return false; + } + + bool open(stream *f, const char *mode, bool needclose) + { + if(file) return false; + for(; *mode; mode++) + { + if(*mode=='r') { reading = true; break; } + else if(*mode=='w') { writing = true; break; } + } + if(!reading && !writing) return false; + + file = f; + + if(reading) checkheader(); + + autoclose = needclose; + return true; + } + + void finishreading() + { + if(!reading) return; + } + + void stopreading() + { + if(!reading) return; + reading = false; + } + + void stopwriting() + { + if(!writing) return; + writing = false; + } + + void close() + { + stopreading(); + stopwriting(); + if(autoclose) DELETEP(file); + } + + bool end() { return !reading && !writing; } + offset tell() { return reading || writing ? pos : offset(-1); } + + bool seek(offset off, int whence) + { + if(writing || !reading) return false; + + if(whence == SEEK_END) + { + uchar skip[512]; + while(read(skip, sizeof(skip)) == sizeof(skip)); + return !off; + } + else if(whence == SEEK_CUR) off += pos; + + if(off >= pos) off -= pos; + else if(off < 0 || !file->seek(0, SEEK_SET)) return false; + else + { + bufread = bufcarry = buflen = 0; + pos = 0; + checkheader(); + } + + uchar skip[512]; + while(off > 0) + { + size_t skipped = (size_t)min(off, (offset)sizeof(skip)); + if(read(skip, skipped) != skipped) { stopreading(); return false; } + off -= skipped; + } + + return true; + } + + size_t read(void *dst, size_t len) + { + if(!reading || !dst || !len) return 0; + size_t next = 0; + while(next < len) + { + if(bufread >= bufcarry) { if(readbuf(BUFSIZE)) continue; stopreading(); break; } + size_t n = min(len - next, bufcarry - bufread); + memcpy(&((uchar *)dst)[next], &buf[bufread], n); + next += n; + bufread += n; + } + pos += next; + return next; + } + + bool getline(char *dst, size_t len) + { + if(!reading || !dst || !len) return false; + --len; + size_t next = 0; + while(next < len) + { + if(bufread >= bufcarry) { if(readbuf(BUFSIZE)) continue; stopreading(); if(!next) return false; break; } + size_t n = min(len - next, bufcarry - bufread); + uchar *endline = (uchar *)memchr(&buf[bufread], '\n', n); + if(endline) { n = endline+1 - &buf[bufread]; len = next + n; } + memcpy(&((uchar *)dst)[next], &buf[bufread], n); + next += n; + bufread += n; + } + dst[next] = '\0'; + pos += next; + return true; + } + + size_t write(const void *src, size_t len) + { + if(!writing || !src || !len) return 0; + uchar dst[512]; + size_t next = 0; + while(next < len) + { + size_t carry = 0, n = encodeutf8(dst, sizeof(dst), &((uchar *)src)[next], len - next, &carry); + if(n > 0 && file->write(dst, n) != n) { stopwriting(); break; } + next += carry; + } + pos += next; + return next; + } + + bool flush() { return file->flush(); } +}; + +stream *openrawfile(const char *filename, const char *mode) +{ + const char *found = findfile(filename, mode); + if(!found) return NULL; + filestream *file = new filestream; + if(!file->open(found, mode)) { delete file; return NULL; } + return file; +} + +stream *openfile(const char *filename, const char *mode) +{ +#ifndef STANDALONE + stream *s = openzipfile(filename, mode); + if(s) return s; +#endif + return openrawfile(filename, mode); +} + +stream *opentempfile(const char *name, const char *mode) +{ + const char *found = findfile(name, mode); + filestream *file = new filestream; + if(!file->opentemp(found ? found : name, mode)) { delete file; return NULL; } + return file; +} + +stream *opengzfile(const char *filename, const char *mode, stream *file, int level) +{ + stream *source = file ? file : openfile(filename, mode); + if(!source) return NULL; + gzstream *gz = new gzstream; + if(!gz->open(source, mode, !file, level)) { if(!file) delete source; delete gz; return NULL; } + return gz; +} + +stream *openutf8file(const char *filename, const char *mode, stream *file) +{ + stream *source = file ? file : openfile(filename, mode); + if(!source) return NULL; + utf8stream *utf8 = new utf8stream; + if(!utf8->open(source, mode, !file)) { if(!file) delete source; delete utf8; return NULL; } + return utf8; +} + +char *loadfile(const char *fn, size_t *size, bool utf8) +{ + stream *f = openfile(fn, "rb"); + if(!f) return NULL; + stream::offset fsize = f->size(); + if(fsize <= 0) { delete f; return NULL; } + size_t len = fsize; + char *buf = new (false) char[len+1]; + if(!buf) { delete f; return NULL; } + size_t offset = 0; + if(utf8 && len >= 3) + { + if(f->read(buf, 3) != 3) { delete f; delete[] buf; return NULL; } + if(((uchar *)buf)[0] == 0xEF && ((uchar *)buf)[1] == 0xBB && ((uchar *)buf)[2] == 0xBF) len -= 3; + else offset += 3; + } + size_t rlen = f->read(&buf[offset], len-offset); + delete f; + if(rlen != len-offset) { delete[] buf; return NULL; } + if(utf8) len = decodeutf8((uchar *)buf, len, (uchar *)buf, len); + buf[len] = '\0'; + if(size!=NULL) *size = len; + return buf; +} + diff --git a/src/shared/tools.cpp b/src/shared/tools.cpp new file mode 100644 index 0000000..ca82e8f --- /dev/null +++ b/src/shared/tools.cpp @@ -0,0 +1,244 @@ +// implementation of generic tools + +#include "cube.h" + +void *operator new(size_t size) +{ + void *p = malloc(size); + if(!p) abort(); + return p; +} + +void *operator new[](size_t size) +{ + void *p = malloc(size); + if(!p) abort(); + return p; +} + +void operator delete(void *p) { if(p) free(p); } + +void operator delete[](void *p) { if(p) free(p); } + +void *operator new(size_t size, bool err) +{ + void *p = malloc(size); + if(!p && err) abort(); + return p; +} + +void *operator new[](size_t size, bool err) +{ + void *p = malloc(size); + if(!p && err) abort(); + return p; +} + +////////////////////////// rnd numbers //////////////////////////////////////// + +#define N (624) +#define M (397) +#define K (0x9908B0DFU) + +static uint state[N]; +static int next = N; + +void seedMT(uint seed) +{ + state[0] = seed; + for(uint i = 1; i < N; i++) + state[i] = seed = 1812433253U * (seed ^ (seed >> 30)) + i; + next = 0; +} + +uint randomMT() +{ + int cur = next; + if(++next >= N) + { + if(next > N) { seedMT(5489U + time(NULL)); cur = next++; } + else next = 0; + } + uint y = (state[cur] & 0x80000000U) | (state[next] & 0x7FFFFFFFU); + state[cur] = y = state[cur < N-M ? cur + M : cur + M-N] ^ (y >> 1) ^ (-int(y & 1U) & K); + y ^= (y >> 11); + y ^= (y << 7) & 0x9D2C5680U; + y ^= (y << 15) & 0xEFC60000U; + y ^= (y >> 18); + return y; +} + +///////////////////////// network /////////////////////// + +// all network traffic is in 32bit ints, which are then compressed using the following simple scheme (assumes that most values are small). + +template<class T> +static inline void putint_(T &p, int n) +{ + if(n<128 && n>-127) p.put(n); + else if(n<0x8000 && n>=-0x8000) { p.put(0x80); p.put(n); p.put(n>>8); } + else { p.put(0x81); p.put(n); p.put(n>>8); p.put(n>>16); p.put(n>>24); } +} +void putint(ucharbuf &p, int n) { putint_(p, n); } +void putint(packetbuf &p, int n) { putint_(p, n); } +void putint(vector<uchar> &p, int n) { putint_(p, n); } + +int getint(ucharbuf &p) +{ + int c = (schar)p.get(); + if(c==-128) { int n = p.get(); n |= ((schar)p.get())<<8; return n; } + else if(c==-127) { int n = p.get(); n |= p.get()<<8; n |= p.get()<<16; return n|(p.get()<<24); } + else return c; +} + +// much smaller encoding for unsigned integers up to 28 bits, but can handle signed +template<class T> +static inline void putuint_(T &p, int n) +{ + if(n < 0 || n >= (1<<21)) + { + p.put(0x80 | (n & 0x7F)); + p.put(0x80 | ((n >> 7) & 0x7F)); + p.put(0x80 | ((n >> 14) & 0x7F)); + p.put(n >> 21); + } + else if(n < (1<<7)) p.put(n); + else if(n < (1<<14)) + { + p.put(0x80 | (n & 0x7F)); + p.put(n >> 7); + } + else + { + p.put(0x80 | (n & 0x7F)); + p.put(0x80 | ((n >> 7) & 0x7F)); + p.put(n >> 14); + } +} +void putuint(ucharbuf &p, int n) { putuint_(p, n); } +void putuint(packetbuf &p, int n) { putuint_(p, n); } +void putuint(vector<uchar> &p, int n) { putuint_(p, n); } + +int getuint(ucharbuf &p) +{ + int n = p.get(); + if(n & 0x80) + { + n += (p.get() << 7) - 0x80; + if(n & (1<<14)) n += (p.get() << 14) - (1<<14); + if(n & (1<<21)) n += (p.get() << 21) - (1<<21); + if(n & (1<<28)) n |= ~0U<<28; + } + return n; +} + +template<class T> +static inline void putfloat_(T &p, float f) +{ + lilswap(&f, 1); + p.put((uchar *)&f, sizeof(float)); +} +void putfloat(ucharbuf &p, float f) { putfloat_(p, f); } +void putfloat(packetbuf &p, float f) { putfloat_(p, f); } +void putfloat(vector<uchar> &p, float f) { putfloat_(p, f); } + +float getfloat(ucharbuf &p) +{ + float f; + p.get((uchar *)&f, sizeof(float)); + return lilswap(f); +} + +template<class T> +static inline void sendstring_(const char *t, T &p) +{ + while(*t) putint(p, *t++); + putint(p, 0); +} +void sendstring(const char *t, ucharbuf &p) { sendstring_(t, p); } +void sendstring(const char *t, packetbuf &p) { sendstring_(t, p); } +void sendstring(const char *t, vector<uchar> &p) { sendstring_(t, p); } + +void getstring(char *text, ucharbuf &p, size_t len) +{ + char *t = text; + do + { + if(t>=&text[len]) { text[len-1] = 0; return; } + if(!p.remaining()) { *t = 0; return; } + *t = getint(p); + } + while(*t++); +} + +void filtertext(char *dst, const char *src, bool whitespace, bool forcespace, size_t len) +{ + for(int c = uchar(*src); c; c = uchar(*++src)) + { + if(c == '\f') + { + if(!*++src) break; + continue; + } + if(!iscubeprint(c)) + { + if(!iscubespace(c) || !whitespace) continue; + if(forcespace) c = ' '; + } + *dst++ = c; + if(!--len) break; + } + *dst = '\0'; +} + +void ipmask::parse(const char *name) +{ + union { uchar b[sizeof(enet_uint32)]; enet_uint32 i; } ipconv, maskconv; + ipconv.i = 0; + maskconv.i = 0; + loopi(4) + { + char *end = NULL; + int n = strtol(name, &end, 10); + if(!end) break; + if(end > name) { ipconv.b[i] = n; maskconv.b[i] = 0xFF; } + name = end; + while(int c = *name) + { + ++name; + if(c == '.') break; + if(c == '/') + { + int range = clamp(int(strtol(name, NULL, 10)), 0, 32); + mask = range ? ENET_HOST_TO_NET_32(0xFFffFFff << (32 - range)) : maskconv.i; + ip = ipconv.i & mask; + return; + } + } + } + ip = ipconv.i; + mask = maskconv.i; +} + +int ipmask::print(char *buf) const +{ + char *start = buf; + union { uchar b[sizeof(enet_uint32)]; enet_uint32 i; } ipconv, maskconv; + ipconv.i = ip; + maskconv.i = mask; + int lastdigit = -1; + loopi(4) if(maskconv.b[i]) + { + if(lastdigit >= 0) *buf++ = '.'; + loopj(i - lastdigit - 1) { *buf++ = '*'; *buf++ = '.'; } + buf += sprintf(buf, "%d", ipconv.b[i]); + lastdigit = i; + } + enet_uint32 bits = ~ENET_NET_TO_HOST_32(mask); + int range = 32; + for(; (bits&0xFF) == 0xFF; bits >>= 8) range -= 8; + for(; bits&1; bits >>= 1) --range; + if(!bits && range%8) buf += sprintf(buf, "/%d", range); + return int(buf-start); +} + diff --git a/src/shared/tools.h b/src/shared/tools.h new file mode 100644 index 0000000..c66f5bd --- /dev/null +++ b/src/shared/tools.h @@ -0,0 +1,1408 @@ +// generic useful stuff for any C++ program + +#ifndef _TOOLS_H +#define _TOOLS_H + +#ifdef NULL +#undef NULL +#endif +#define NULL 0 + +typedef signed char schar; +typedef unsigned char uchar; +typedef unsigned short ushort; +typedef unsigned int uint; +typedef unsigned long ulong; +typedef signed long long int llong; +typedef unsigned long long int ullong; + +#ifdef _DEBUG +#define ASSERT(c) assert(c) +#else +#define ASSERT(c) if(c) {} +#endif + +#if defined(__GNUC__) || (defined(_MSC_VER) && _MSC_VER >= 1400) +#define RESTRICT __restrict +#else +#define RESTRICT +#endif + +#ifdef __GNUC__ +#define UNUSED __attribute__((unused)) +#else +#define UNUSED +#endif + +void *operator new(size_t, bool); +void *operator new[](size_t, bool); +inline void *operator new(size_t, void *p) { return p; } +inline void *operator new[](size_t, void *p) { return p; } +inline void operator delete(void *, void *) {} +inline void operator delete[](void *, void *) {} + +#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<class T, class U> +static inline T clamp(T a, U b, U c) +{ + return max(T(b), min(a, T(c))); +} + +#ifdef __GNUC__ +#define bitscan(mask) (__builtin_ffs(mask)-1) +#else +#ifdef WIN32 +#pragma intrinsic(_BitScanForward) +static inline int bitscan(uint mask) +{ + ulong i; + return _BitScanForward(&i, mask) ? i : -1; +} +#else +static inline int bitscan(uint mask) +{ + if(!mask) return -1; + int i = 1; + if(!(mask&0xFFFF)) { i += 16; mask >>= 16; } + if(!(mask&0xFF)) { i += 8; mask >>= 8; } + if(!(mask&0xF)) { i += 4; mask >>= 4; } + if(!(mask&3)) { i += 2; mask >>= 2; } + return i - (mask&1); +} +#endif +#endif + +#define rnd(x) ((int)(randomMT()&0x7FFFFFFF)%(x)) +#define rndscale(x) (float((randomMT()&0x7FFFFFFF)*double(x)/double(0x7FFFFFFF))) +#define detrnd(s, x) ((int)(((((uint)(s))*1103515245+12345)>>16)%(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 looprev(v,m) for(int v = int(m); --v >= 0;) +#define loopirev(m) looprev(i,m) +#define loopjrev(m) looprev(j,m) +#define loopkrev(m) looprev(k,m) +#define looplrev(m) looprev(l,m) + +#define DELETEP(p) if(p) { delete p; p = 0; } +#define DELETEA(p) if(p) { delete[] p; p = 0; } + +#define PI (3.1415927f) +#define PI2 (2*PI) +#define SQRT2 (1.4142136f) +#define SQRT3 (1.7320508f) +#define RAD (PI / 180.0f) + +#ifdef WIN32 +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif +#ifndef M_LN2 +#define M_LN2 0.693147180559945309417 +#endif + +#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: 4267) // conversion from 'size_t' to 'int', 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 _stricmp +#define strncasecmp _strnicmp +#define PATHDIV '\\' + +#else +#define __cdecl +#define _vsnprintf vsnprintf +#define PATHDIV '/' +#endif + +#ifdef __GNUC__ +#define PRINTFARGS(fmt, args) __attribute__((format(printf, fmt, args))) +#else +#define PRINTFARGS(fmt, args) +#endif + +// easy safe strings + +#define MAXSTRLEN 260 +typedef char string[MAXSTRLEN]; + +inline void vformatstring(char *d, const char *fmt, va_list v, int len) { _vsnprintf(d, len, fmt, v); d[len-1] = 0; } +template<size_t N> inline void vformatstring(char (&d)[N], const char *fmt, va_list v) { vformatstring(d, fmt, v, N); } + +inline char *copystring(char *d, const char *s, size_t len) +{ + size_t slen = min(strlen(s), len-1); + memcpy(d, s, slen); + d[slen] = 0; + return d; +} +template<size_t N> inline char *copystring(char (&d)[N], const char *s) { return copystring(d, s, N); } + +inline char *concatstring(char *d, const char *s, size_t len) { size_t used = strlen(d); return used < len ? copystring(d+used, s, len-used) : d; } +template<size_t N> inline char *concatstring(char (&d)[N], const char *s) { return concatstring(d, s, N); } + +inline char *prependstring(char *d, const char *s, size_t len) +{ + size_t slen = min(strlen(s), len); + memmove(&d[slen], d, min(len - slen, strlen(d) + 1)); + memcpy(d, s, slen); + d[len-1] = 0; + return d; +} +template<size_t N> inline char *prependstring(char (&d)[N], const char *s) { return prependstring(d, s, N); } + +inline void nformatstring(char *d, int len, const char *fmt, ...) PRINTFARGS(3, 4); +inline void nformatstring(char *d, int len, const char *fmt, ...) +{ + va_list v; + va_start(v, fmt); + vformatstring(d, fmt, v, len); + va_end(v); +} + +template<size_t N> inline void formatstring(char (&d)[N], const char *fmt, ...) PRINTFARGS(2, 3); +template<size_t N> inline void formatstring(char (&d)[N], const char *fmt, ...) +{ + va_list v; + va_start(v, fmt); + vformatstring(d, fmt, v, int(N)); + va_end(v); +} + +template<size_t N> inline void concformatstring(char (&d)[N], const char *fmt, ...) PRINTFARGS(2, 3); +template<size_t N> inline void concformatstring(char (&d)[N], const char *fmt, ...) +{ + va_list v; + va_start(v, fmt); + int len = strlen(d); + vformatstring(d + len, fmt, v, int(N) - len); + va_end(v); +} + +#define defformatstring(d,...) string d; formatstring(d, __VA_ARGS__) +#define defvformatstring(d,last,fmt) string d; { va_list ap; va_start(ap, last); vformatstring(d, fmt, ap); va_end(ap); } + +template<size_t N> inline bool matchstring(const char *s, size_t len, const char (&d)[N]) +{ + return len == N-1 && !memcmp(s, d, N-1); +} + +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; } + +#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--) + +template<class T> inline void memclear(T *p, size_t n) { memset((void *)p, 0, n * sizeof(T)); } +template<class T> inline void memclear(T &p) { memset((void *)&p, 0, sizeof(T)); } +template<class T, size_t N> inline void memclear(T (&p)[N]) { memset((void *)p, 0, N * sizeof(T)); } + +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) {} + + void reset() + { + len = 0; + flags = 0; + } + + void reset(T *buf_, int maxlen_) + { + reset(); + buf = buf_; + maxlen = maxlen_; + } + + const T &get() + { + static T overreadval = 0; + if(len<maxlen) return buf[len++]; + flags |= OVERREAD; + return overreadval; + } + + databuf subbuf(int sz) + { + sz = clamp(sz, 0, maxlen-len); + len += sz; + return databuf(&buf[len-sz], sz); + } + + T *pad(int numvals) + { + T *vals = &buf[len]; + len += min(numvals, maxlen-len); + return vals; + } + + 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], (const void *)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, (void *)&buf[len], read*sizeof(T)); + len += read; + return read; + } + + void offset(int n) + { + n = min(n, maxlen); + buf += n; + maxlen -= n; + len = max(len-n, 0); + } + + T *getbuf() const { return buf; } + bool empty() const { return len==0; } + 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; } + + bool check(int n) { return remaining() >= n; } + + 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); + } + + 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> +static inline float heapscore(const T &n) { return n; } + +struct sortless +{ + template<class T> bool operator()(const T &x, const T &y) const { return x < y; } + bool operator()(char *x, char *y) const { return strcmp(x, y) < 0; } + bool operator()(const char *x, const char *y) const { return strcmp(x, y) < 0; } +}; + +struct sortnameless +{ + template<class T> bool operator()(const T &x, const T &y) const { return sortless()(x.name, y.name); } + template<class T> bool operator()(T *x, T *y) const { return sortless()(x->name, y->name); } + template<class T> bool operator()(const T *x, const T *y) const { return sortless()(x->name, y->name); } +}; + +template<class T, class F> +static inline void insertionsort(T *start, T *end, F fun) +{ + for(T *i = start+1; i < end; i++) + { + if(fun(*i, i[-1])) + { + T tmp = *i; + *i = i[-1]; + T *j = i-1; + for(; j > start && fun(tmp, j[-1]); --j) + *j = j[-1]; + *j = tmp; + } + } + +} + +template<class T, class F> +static inline void insertionsort(T *buf, int n, F fun) +{ + insertionsort(buf, buf+n, fun); +} + +template<class T> +static inline void insertionsort(T *buf, int n) +{ + insertionsort(buf, buf+n, sortless()); +} + +template<class T, class F> +static inline void quicksort(T *start, T *end, F fun) +{ + while(end-start > 10) + { + T *mid = &start[(end-start)/2], *i = start+1, *j = end-2, pivot; + if(fun(*start, *mid)) /* start < mid */ + { + if(fun(end[-1], *start)) { pivot = *start; *start = end[-1]; end[-1] = *mid; } /* end < start < mid */ + else if(fun(end[-1], *mid)) { pivot = end[-1]; end[-1] = *mid; } /* start <= end < mid */ + else { pivot = *mid; } /* start < mid <= end */ + } + else if(fun(*start, end[-1])) { pivot = *start; *start = *mid; } /*mid <= start < end */ + else if(fun(*mid, end[-1])) { pivot = end[-1]; end[-1] = *start; *start = *mid; } /* mid < end <= start */ + else { pivot = *mid; swap(*start, end[-1]); } /* end <= mid <= start */ + *mid = end[-2]; + do + { + while(fun(*i, pivot)) if(++i >= j) goto partitioned; + while(fun(pivot, *--j)) if(i >= j) goto partitioned; + swap(*i, *j); + } + while(++i < j); + partitioned: + end[-2] = *i; + *i = pivot; + + if(i-start < end-(i+1)) + { + quicksort(start, i, fun); + start = i+1; + } + else + { + quicksort(i+1, end, fun); + end = i; + } + } + + insertionsort(start, end, fun); +} + +template<class T, class F> +static inline void quicksort(T *buf, int n, F fun) +{ + quicksort(buf, buf+n, fun); +} + +template<class T> +static inline void quicksort(T *buf, int n) +{ + quicksort(buf, buf+n, sortless()); +} + +template<class T> struct isclass +{ + template<class C> static char test(void (C::*)(void)); + template<class C> static int test(...); + enum { yes = sizeof(test<T>(0)) == 1 ? 1 : 0, no = yes^1 }; +}; + +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 stringslice +{ + const char *str; + int len; + stringslice() {} + stringslice(const char *str, int len) : str(str), len(len) {} + stringslice(const char *str, const char *end) : str(str), len(int(end-str)) {} + + const char *end() const { return &str[len]; } +}; + +inline char *newstring(const stringslice &s) { return newstring(s.str, s.len); } +inline const char *stringptr(const char *s) { return s; } +inline const char *stringptr(const stringslice &s) { return s.str; } +inline int stringlen(const char *s) { return int(strlen(s)); } +inline int stringlen(const stringslice &s) { return s.len; } + +inline char *copystring(char *d, const stringslice &s, size_t len) +{ + size_t slen = min(size_t(s.len), len-1); + memcpy(d, s.str, slen); + d[slen] = 0; + return d; +} +template<size_t N> inline char *copystring(char (&d)[N], const stringslice &s) { return copystring(d, s, N); } + +static inline uint memhash(const void *ptr, int len) +{ + const uchar *data = (const uchar *)ptr; + uint h = 5381; + loopi(len) h = ((h<<5)+h)^data[i]; + return h; +} + +static inline uint hthash(const stringslice &s) { return memhash(s.str, s.len); } + +static inline bool htcmp(const stringslice &x, const char *y) +{ + return x.len == (int)strlen(y) && !memcmp(x.str, y, x.len); +} + +static inline uint hthash(int key) +{ + return key; +} + +static inline bool htcmp(int x, int y) +{ + return x==y; +} + +#ifndef STANDALONE +static inline uint hthash(GLuint key) +{ + return key; +} + +static inline bool htcmp(GLuint x, GLuint y) +{ + return x==y; +} +#endif + +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++]; + } + + void move(vector<T> &v) + { + if(!ulen) + { + swap(buf, v.buf); + swap(ulen, v.ulen); + swap(alen, v.alen); + } + else + { + growbuf(ulen+v.ulen); + if(v.ulen) memcpy(&buf[ulen], (void *)v.buf, v.ulen*sizeof(T)); + ulen += v.ulen; + v.ulen = 0; + } + } + + bool inrange(size_t i) const { return i<size_t(ulen); } + bool inrange(int i) const { return i>=0 && i<ulen; } + + T &pop() { return buf[--ulen]; } + T &last() { return buf[ulen-1]; } + void drop() { ulen--; 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 disown() { buf = NULL; alen = ulen = 0; } + + void shrink(int i) { ASSERT(i<=ulen); if(isclass<T>::no) ulen = i; else while(ulen>i) drop(); } + void setsize(int i) { ASSERT(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 F> + void sort(F fun, int i = 0, int n = -1) + { + quicksort(&buf[i], n < 0 ? ulen-i : n, fun); + } + + void sort() { sort(sortless()); } + void sortname() { sort(sortnameless()); } + + void growbuf(int sz) + { + int olen = alen; + if(alen <= 0) alen = max(MINSIZE, sz); + else while(alen < sz) alen += alen/2; + if(alen <= olen) return; + uchar *newbuf = new uchar[alen*sizeof(T)]; + if(olen > 0) + { + if(ulen > 0) memcpy(newbuf, (void *)buf, ulen*sizeof(T)); + delete[] (uchar *)buf; + } + buf = (T *)newbuf; + } + + databuf<T> reserve(int sz) + { + if(alen-ulen < sz) 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; + } + + T removeunordered(int i) + { + T e = buf[i]; + ulen--; + if(ulen>0) buf[i] = buf[ulen]; + return e; + } + + template<class U> + int find(const U &o) + { + loopi(ulen) if(buf[i]==o) return i; + return -1; + } + + void addunique(const T &o) + { + if(find(o) < 0) add(o); + } + + void removeobj(const T &o) + { + loopi(ulen) if(buf[i] == o) + { + int dst = i; + for(int j = i+1; j < ulen; j++) if(!(buf[j] == o)) buf[dst++] = buf[j]; + setsize(dst); + break; + } + } + + void replacewithlast(const T &o) + { + if(!ulen) return; + loopi(ulen-1) if(buf[i]==o) + { + buf[i] = buf[ulen-1]; + break; + } + 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(alen-ulen < n) 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]; + } + + void reverse() + { + loopi(ulen/2) swap(buf[i], buf[ulen-1-i]); + } + + static int heapparent(int i) { return (i - 1) >> 1; } + static int heapchild(int i) { return (i << 1) + 1; } + + void buildheap() + { + for(int i = ulen/2; i >= 0; i--) downheap(i); + } + + int upheap(int i) + { + float score = heapscore(buf[i]); + while(i > 0) + { + int pi = heapparent(i); + if(score >= heapscore(buf[pi])) break; + swap(buf[i], buf[pi]); + i = pi; + } + return i; + } + + T &addheap(const T &x) + { + add(x); + return buf[upheap(ulen-1)]; + } + + int downheap(int i) + { + float score = heapscore(buf[i]); + for(;;) + { + int ci = heapchild(i); + if(ci >= ulen) break; + float cscore = heapscore(buf[ci]); + if(score > cscore) + { + if(ci+1 < ulen && heapscore(buf[ci+1]) < cscore) { swap(buf[ci+1], buf[i]); i = ci+1; } + else { swap(buf[ci], buf[i]); i = ci; } + } + else if(ci+1 < ulen && heapscore(buf[ci+1]) < score) { swap(buf[ci+1], buf[i]); i = ci+1; } + else break; + } + return i; + } + + T removeheap() + { + T e = removeunordered(0); + if(ulen) downheap(0); + return e; + } + + template<class K> + int htfind(const K &key) + { + loopi(ulen) if(htcmp(key, buf[i])) return i; + return -1; + } +}; + +template<class H, class E, class K, class T> struct hashbase +{ + typedef E elemtype; + typedef K keytype; + typedef T datatype; + + enum { CHUNKSIZE = 64 }; + + struct chain { E elem; chain *next; }; + struct chainchunk { chain chains[CHUNKSIZE]; chainchunk *next; }; + + int size; + int numelems; + chain **chains; + + chainchunk *chunks; + chain *unused; + + enum { DEFAULTSIZE = 1<<10 }; + + hashbase(int size = DEFAULTSIZE) + : size(size) + { + numelems = 0; + chunks = NULL; + unused = NULL; + chains = new chain *[size]; + memset(chains, 0, size*sizeof(chain *)); + } + + ~hashbase() + { + DELETEA(chains); + deletechunks(); + } + + chain *insert(uint h) + { + if(!unused) + { + chainchunk *chunk = new chainchunk; + chunk->next = chunks; + chunks = chunk; + loopi(CHUNKSIZE-1) chunk->chains[i].next = &chunk->chains[i+1]; + chunk->chains[CHUNKSIZE-1].next = unused; + unused = chunk->chains; + } + chain *c = unused; + unused = unused->next; + c->next = chains[h]; + chains[h] = c; + numelems++; + return c; + } + + template<class U> + T &insert(uint h, const U &key) + { + chain *c = insert(h); + H::setkey(c->elem, key); + return H::getdata(c->elem); + } + + #define HTFIND(success, fail) \ + uint h = hthash(key)&(this->size-1); \ + for(chain *c = this->chains[h]; c; c = c->next) \ + { \ + if(htcmp(key, H::getkey(c->elem))) return success H::getdata(c->elem); \ + } \ + return (fail); + + template<class U> + T *access(const U &key) + { + HTFIND(&, NULL); + } + + template<class U, class V> + T &access(const U &key, const V &elem) + { + HTFIND( , insert(h, key) = elem); + } + + template<class U> + T &operator[](const U &key) + { + HTFIND( , insert(h, key)); + } + + template<class U> + T &find(const U &key, T ¬found) + { + HTFIND( , notfound); + } + + template<class U> + const T &find(const U &key, const T ¬found) + { + HTFIND( , notfound); + } + + template<class U> + bool remove(const U &key) + { + uint h = hthash(key)&(size-1); + for(chain **p = &chains[h], *c = chains[h]; c; p = &c->next, c = c->next) + { + if(htcmp(key, H::getkey(c->elem))) + { + *p = c->next; + c->elem.~E(); + new (&c->elem) E; + 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() + { + if(!numelems) return; + memset(chains, 0, size*sizeof(chain *)); + numelems = 0; + unused = NULL; + deletechunks(); + } + + static inline chain *enumnext(void *i) { return ((chain *)i)->next; } + static inline K &enumkey(void *i) { return H::getkey(((chain *)i)->elem); } + static inline T &enumdata(void *i) { return H::getdata(((chain *)i)->elem); } +}; + +template<class T> struct hashset : hashbase<hashset<T>, T, T, T> +{ + typedef hashbase<hashset<T>, T, T, T> basetype; + + hashset(int size = basetype::DEFAULTSIZE) : basetype(size) {} + + static inline const T &getkey(const T &elem) { return elem; } + static inline T &getdata(T &elem) { return elem; } + template<class K> static inline void setkey(T &elem, const K &key) {} + + template<class V> + T &add(const V &elem) + { + return basetype::access(elem, elem); + } +}; + +template<class T> struct hashnameset : hashbase<hashnameset<T>, T, const char *, T> +{ + typedef hashbase<hashnameset<T>, T, const char *, T> basetype; + + hashnameset(int size = basetype::DEFAULTSIZE) : basetype(size) {} + + template<class U> static inline const char *getkey(const U &elem) { return elem.name; } + template<class U> static inline const char *getkey(U *elem) { return elem->name; } + static inline T &getdata(T &elem) { return elem; } + template<class K> static inline void setkey(T &elem, const K &key) {} + + template<class V> + T &add(const V &elem) + { + return basetype::access(getkey(elem), elem); + } +}; + +template<class K, class T> struct hashtableentry +{ + K key; + T data; +}; + +template<class K, class T> struct hashtable : hashbase<hashtable<K, T>, hashtableentry<K, T>, K, T> +{ + typedef hashbase<hashtable<K, T>, hashtableentry<K, T>, K, T> basetype; + typedef typename basetype::elemtype elemtype; + + hashtable(int size = basetype::DEFAULTSIZE) : basetype(size) {} + + static inline K &getkey(elemtype &elem) { return elem.key; } + static inline T &getdata(elemtype &elem) { return elem.data; } + template<class U> static inline void setkey(elemtype &elem, const U &key) { elem.key = key; } +}; + +#define enumeratekt(ht,k,e,t,f,b) loopi((ht).size) for(void *ec = (ht).chains[i]; ec;) { k &e = (ht).enumkey(ec); t &f = (ht).enumdata(ec); ec = (ht).enumnext(ec); b; } +#define enumerate(ht,t,e,b) loopi((ht).size) for(void *ec = (ht).chains[i]; ec;) { t &e = (ht).enumdata(ec); ec = (ht).enumnext(ec); b; } + +struct unionfind +{ + struct ufval + { + int rank, next; + + ufval() : rank(0), next(-1) {} + }; + + vector<ufval> ufvals; + + int find(int k) + { + if(k>=ufvals.length()) return k; + while(ufvals[k].next>=0) k = ufvals[k].next; + return k; + } + + int compressfind(int k) + { + if(ufvals[k].next<0) return k; + return ufvals[k].next = compressfind(ufvals[k].next); + } + + void unite (int x, int y) + { + while(ufvals.length() <= max(x, y)) ufvals.add(); + x = compressfind(x); + y = compressfind(y); + if(x==y) return; + ufval &xval = ufvals[x], &yval = ufvals[y]; + if(xval.rank < yval.rank) xval.next = y; + else + { + yval.next = x; + if(xval.rank==yval.rank) yval.rank++; + } + } +}; + +template <class T, int SIZE> struct queue +{ + int head, tail, len; + T data[SIZE]; + + queue() { clear(); } + + void clear() { head = tail = len = 0; } + + int capacity() const { return SIZE; } + int length() const { return len; } + bool empty() const { return !len; } + bool full() const { return len == SIZE; } + + bool inrange(size_t i) const { return i<size_t(len); } + bool inrange(int i) const { return i>=0 && i<len; } + + T &added() { return data[tail > 0 ? tail-1 : SIZE-1]; } + T &added(int offset) { return data[tail-offset > 0 ? tail-offset-1 : tail-offset-1 + SIZE]; } + T &adding() { return data[tail]; } + T &adding(int offset) { return data[tail+offset >= SIZE ? tail+offset - SIZE : tail+offset]; } + T &add() + { + T &t = data[tail]; + tail++; + if(tail >= SIZE) tail -= SIZE; + if(len < SIZE) len++; + return t; + } + T &add(const T &e) { return add() = e; } + + databuf<T> reserve(int sz) + { + if(!len) head = tail = 0; + return databuf<T>(&data[tail], min(sz, SIZE-tail)); + } + + void advance(int sz) + { + if(len + sz > SIZE) sz = SIZE - len; + tail += sz; + if(tail >= SIZE) tail -= SIZE; + len += sz; + } + + void addbuf(const databuf<T> &p) + { + advance(p.length()); + } + + T &pop() + { + tail--; + if(tail < 0) tail += SIZE; + len--; + return data[tail]; + } + + T &removing() { return data[head]; } + T &removing(int offset) { return data[head+offset >= SIZE ? head+offset - SIZE : head+offset]; } + T &remove() + { + T &t = data[head]; + head++; + if(head >= SIZE) head -= SIZE; + len--; + return t; + } + + T remove(int offset) + { + T val = removing(offset); + if(head+offset >= SIZE) for(int i = head+offset - SIZE + 1; i < tail; i++) data[i-1] = data[i]; + else if(head < tail) for(int i = head+offset + 1; i < tail; i++) data[i-1] = data[i]; + else + { + for(int i = head+offset + 1; i < SIZE; i++) data[i-1] = data[i]; + data[SIZE-1] = data[0]; + for(int i = 1; i < tail; i++) data[i-1] = data[i]; + } + tail--; + if(tail < 0) tail += SIZE; + len--; + return val; + } + + T &operator[](int offset) { return removing(offset); } + const T &operator[](int offset) const { return removing(offset); } +}; + +template <class T, int SIZE> struct reversequeue : queue<T, SIZE> +{ + T &operator[](int offset) { return queue<T, SIZE>::added(offset); } + const T &operator[](int offset) const { return queue<T, SIZE>::added(offset); } +}; + +const int islittleendian = 1; +#ifdef SDL_BYTEORDER +#define endianswap16 SDL_Swap16 +#define endianswap32 SDL_Swap32 +#define endianswap64 SDL_Swap64 +#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 ullong endianswap64(ullong n) { return endianswap32(uint(n >> 32)) | ((ullong)endianswap32(uint(n)) << 32); } +#endif +template<class T> inline T endianswap(T n) { union { T t; uint i; } conv; conv.t = n; conv.i = endianswap32(conv.i); return conv.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 ullong endianswap<ullong>(ullong n) { return endianswap64(n); } +template<> inline llong endianswap<llong>(llong n) { return endianswap64(n); } +template<> inline double endianswap<double>(double n) { union { double t; uint i; } conv; conv.t = n; conv.i = endianswap64(conv.i); return conv.t; } +template<class T> inline void endianswap(T *buf, size_t 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, size_t len) {} +#ifdef SDL_BYTEORDER +#if SDL_BYTEORDER == SDL_LIL_ENDIAN +#define lilswap endiansame +#define bigswap endianswap +#else +#define lilswap endianswap +#define bigswap endiansame +#endif +#elif defined(__BYTE_ORDER__) +#if __BYTE_ORDER__ == __ORDER_LITTLE_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, size_t 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, size_t 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 +{ +#ifdef WIN32 +#if defined(__GNUC__) && !defined(__MINGW32__) + typedef off64_t offset; +#else + typedef __int64 offset; +#endif +#else + typedef off_t offset; +#endif + + virtual ~stream() {} + virtual void close() = 0; + virtual bool end() = 0; + virtual offset tell() { return -1; } + virtual offset rawtell() { return tell(); } + virtual bool seek(offset pos, int whence = SEEK_SET) { return false; } + virtual offset size(); + virtual offset rawsize() { return size(); } + virtual size_t read(void *buf, size_t len) { return 0; } + virtual size_t write(const void *buf, size_t len) { return 0; } + virtual bool flush() { return true; } + 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, size_t len); + virtual bool putstring(const char *str) { size_t len = strlen(str); return write(str, len) == len; } + virtual bool putline(const char *str) { return putstring(str) && putchar('\n'); } + virtual size_t printf(const char *fmt, ...) PRINTFARGS(2, 3); + virtual uint getcrc() { return 0; } + + template<class T> size_t put(const T *v, size_t n) { return write(v, n*sizeof(T))/sizeof(T); } + 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> size_t get(T *v, size_t n) { return read(v, n*sizeof(T))/sizeof(T); } + 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 +}; + +template<class T> +struct streambuf +{ + stream *s; + + streambuf(stream *s) : s(s) {} + + T get() { return s->get<T>(); } + size_t get(T *vals, size_t numvals) { return s->get(vals, numvals); } + void put(const T &val) { s->put(&val, 1); } + void put(const T *vals, size_t numvals) { s->put(vals, numvals); } + size_t length() { return s->size(); } +}; + +enum +{ + CT_PRINT = 1<<0, + CT_SPACE = 1<<1, + CT_DIGIT = 1<<2, + CT_ALPHA = 1<<3, + CT_LOWER = 1<<4, + CT_UPPER = 1<<5, + CT_UNICODE = 1<<6 +}; +extern const uchar cubectype[256]; +static inline int iscubeprint(uchar c) { return cubectype[c]&CT_PRINT; } +static inline int iscubespace(uchar c) { return cubectype[c]&CT_SPACE; } +static inline int iscubealpha(uchar c) { return cubectype[c]&CT_ALPHA; } +static inline int iscubealnum(uchar c) { return cubectype[c]&(CT_ALPHA|CT_DIGIT); } +static inline int iscubelower(uchar c) { return cubectype[c]&CT_LOWER; } +static inline int iscubeupper(uchar c) { return cubectype[c]&CT_UPPER; } +static inline int iscubepunct(uchar c) { return cubectype[c] == CT_PRINT; } +static inline int cube2uni(uchar c) +{ + extern const int cube2unichars[256]; + return cube2unichars[c]; +} +static inline uchar uni2cube(int c) +{ + extern const int uni2cubeoffsets[8]; + extern const uchar uni2cubechars[]; + return uint(c) <= 0x7FF ? uni2cubechars[uni2cubeoffsets[c>>8] + (c&0xFF)] : 0; +} +static inline uchar cubelower(uchar c) +{ + extern const uchar cubelowerchars[256]; + return cubelowerchars[c]; +} +static inline uchar cubeupper(uchar c) +{ + extern const uchar cubeupperchars[256]; + return cubeupperchars[c]; +} +extern size_t decodeutf8(uchar *dst, size_t dstlen, const uchar *src, size_t srclen, size_t *carry = NULL); +extern size_t encodeutf8(uchar *dstbuf, size_t dstlen, const uchar *srcbuf, size_t srclen, size_t *carry = NULL); + +extern string homedir; + +extern char *makerelpath(const char *dir, const char *file, const char *prefix = NULL, const char *cmd = NULL); +extern char *path(char *s); +extern char *path(const char *s, bool copy); +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 const char *sethomedir(const char *dir); +extern const char *addpackagedir(const char *dir); +extern const char *findfile(const char *filename, const char *mode); +extern bool findzipfile(const char *filename); +extern stream *openrawfile(const char *filename, const char *mode); +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 stream *openutf8file(const char *filename, const char *mode, stream *file = NULL); +extern char *loadfile(const char *fn, size_t *size, bool utf8 = true); +extern bool listdir(const char *dir, bool rel, const char *ext, vector<char *> &files); +extern int listfiles(const char *dir, const char *ext, vector<char *> &files); +extern int listzipfiles(const char *dir, const char *ext, vector<char *> &files); +extern void seedMT(uint seed); +extern uint randomMT(); + +extern void putint(ucharbuf &p, int n); +extern void putint(packetbuf &p, int n); +extern void putint(vector<uchar> &p, int n); +extern int getint(ucharbuf &p); +extern void putuint(ucharbuf &p, int n); +extern void putuint(packetbuf &p, int n); +extern void putuint(vector<uchar> &p, int n); +extern int getuint(ucharbuf &p); +extern void putfloat(ucharbuf &p, float f); +extern void putfloat(packetbuf &p, float f); +extern void putfloat(vector<uchar> &p, float f); +extern float getfloat(ucharbuf &p); +extern void sendstring(const char *t, ucharbuf &p); +extern void sendstring(const char *t, packetbuf &p); +extern void sendstring(const char *t, vector<uchar> &p); +extern void getstring(char *t, ucharbuf &p, size_t len); +template<size_t N> static inline void getstring(char (&t)[N], ucharbuf &p) { getstring(t, p, N); } +extern void filtertext(char *dst, const char *src, bool whitespace, bool forcespace, size_t len); +template<size_t N> static inline void filtertext(char (&dst)[N], const char *src, bool whitespace = true, bool forcespace = false) { filtertext(dst, src, whitespace, forcespace, N-1); } + +struct ipmask +{ + enet_uint32 ip, mask; + + void parse(const char *name); + int print(char *buf) const; + bool check(enet_uint32 host) const { return (host & mask) == ip; } +}; + +#endif + diff --git a/src/shared/zip.cpp b/src/shared/zip.cpp new file mode 100644 index 0000000..c35fa87 --- /dev/null +++ b/src/shared/zip.cpp @@ -0,0 +1,588 @@ +#include "cube.h" + +enum +{ + ZIP_LOCAL_FILE_SIGNATURE = 0x04034B50, + ZIP_LOCAL_FILE_SIZE = 30, + ZIP_FILE_SIGNATURE = 0x02014B50, + ZIP_FILE_SIZE = 46, + ZIP_DIRECTORY_SIGNATURE = 0x06054B50, + ZIP_DIRECTORY_SIZE = 22 +}; + +struct ziplocalfileheader +{ + uint signature; + ushort version, flags, compression, modtime, moddate; + uint crc32, compressedsize, uncompressedsize; + ushort namelength, extralength; +}; + +struct zipfileheader +{ + uint signature; + ushort version, needversion, flags, compression, modtime, moddate; + uint crc32, compressedsize, uncompressedsize; + ushort namelength, extralength, commentlength, disknumber, internalattribs; + uint externalattribs, offset; +}; + +struct zipdirectoryheader +{ + uint signature; + ushort disknumber, directorydisk, diskentries, entries; + uint size, offset; + ushort commentlength; +}; + +struct zipfile +{ + char *name; + uint header, offset, size, compressedsize; + + zipfile() : name(NULL), header(0), offset(~0U), size(0), compressedsize(0) + { + } + ~zipfile() + { + DELETEA(name); + } +}; + +struct zipstream; + +struct ziparchive +{ + char *name; + FILE *data; + hashnameset<zipfile> files; + int openfiles; + zipstream *owner; + + ziparchive() : name(NULL), data(NULL), files(512), openfiles(0), owner(NULL) + { + } + ~ziparchive() + { + DELETEA(name); + if(data) { fclose(data); data = NULL; } + } +}; + +static bool findzipdirectory(FILE *f, zipdirectoryheader &hdr) +{ + if(fseek(f, 0, SEEK_END) < 0) return false; + + long offset = ftell(f); + if(offset < 0) return false; + + uchar buf[1024], *src = NULL; + long end = max(offset - 0xFFFFL - ZIP_DIRECTORY_SIZE, 0L); + size_t len = 0; + const uint signature = lilswap<uint>(ZIP_DIRECTORY_SIGNATURE); + + while(offset > end) + { + size_t carry = min(len, size_t(ZIP_DIRECTORY_SIZE-1)), next = min(sizeof(buf) - carry, size_t(offset - end)); + offset -= next; + memmove(&buf[next], buf, carry); + if(next + carry < ZIP_DIRECTORY_SIZE || fseek(f, offset, SEEK_SET) < 0 || fread(buf, 1, next, f) != next) return false; + len = next + carry; + uchar *search = &buf[next-1]; + for(; search >= buf; search--) if(*(uint *)search == signature) break; + if(search >= buf) { src = search; break; } + } + + if(!src || &buf[len] - src < ZIP_DIRECTORY_SIZE) return false; + + hdr.signature = lilswap(*(uint *)src); src += 4; + hdr.disknumber = lilswap(*(ushort *)src); src += 2; + hdr.directorydisk = lilswap(*(ushort *)src); src += 2; + hdr.diskentries = lilswap(*(ushort *)src); src += 2; + hdr.entries = lilswap(*(ushort *)src); src += 2; + hdr.size = lilswap(*(uint *)src); src += 4; + hdr.offset = lilswap(*(uint *)src); src += 4; + hdr.commentlength = lilswap(*(ushort *)src); src += 2; + + if(hdr.signature != ZIP_DIRECTORY_SIGNATURE || hdr.disknumber != hdr.directorydisk || hdr.diskentries != hdr.entries) return false; + + return true; +} + +#ifndef STANDALONE +VAR(dbgzip, 0, 0, 1); +#endif + +static bool readzipdirectory(const char *archname, FILE *f, int entries, int offset, uint size, vector<zipfile> &files) +{ + uchar *buf = new (false) uchar[size], *src = buf; + if(!buf || fseek(f, offset, SEEK_SET) < 0 || fread(buf, 1, size, f) != size) { delete[] buf; return false; } + loopi(entries) + { + if(src + ZIP_FILE_SIZE > &buf[size]) break; + + zipfileheader hdr; + hdr.signature = lilswap(*(uint *)src); src += 4; + hdr.version = lilswap(*(ushort *)src); src += 2; + hdr.needversion = lilswap(*(ushort *)src); src += 2; + hdr.flags = lilswap(*(ushort *)src); src += 2; + hdr.compression = lilswap(*(ushort *)src); src += 2; + hdr.modtime = lilswap(*(ushort *)src); src += 2; + hdr.moddate = lilswap(*(ushort *)src); src += 2; + hdr.crc32 = lilswap(*(uint *)src); src += 4; + hdr.compressedsize = lilswap(*(uint *)src); src += 4; + hdr.uncompressedsize = lilswap(*(uint *)src); src += 4; + hdr.namelength = lilswap(*(ushort *)src); src += 2; + hdr.extralength = lilswap(*(ushort *)src); src += 2; + hdr.commentlength = lilswap(*(ushort *)src); src += 2; + hdr.disknumber = lilswap(*(ushort *)src); src += 2; + hdr.internalattribs = lilswap(*(ushort *)src); src += 2; + hdr.externalattribs = lilswap(*(uint *)src); src += 4; + hdr.offset = lilswap(*(uint *)src); src += 4; + if(hdr.signature != ZIP_FILE_SIGNATURE) break; + if(!hdr.namelength || !hdr.uncompressedsize || (hdr.compression && (hdr.compression != Z_DEFLATED || !hdr.compressedsize))) + { + src += hdr.namelength + hdr.extralength + hdr.commentlength; + continue; + } + if(src + hdr.namelength > &buf[size]) break; + + string pname; + int namelen = min((int)hdr.namelength, (int)sizeof(pname)-1); + memcpy(pname, src, namelen); + pname[namelen] = '\0'; + path(pname); + char *name = newstring(pname); + + zipfile &f = files.add(); + f.name = name; + f.header = hdr.offset; + f.size = hdr.uncompressedsize; + f.compressedsize = hdr.compression ? hdr.compressedsize : 0; +#ifndef STANDALONE + if(dbgzip) conoutf(CON_DEBUG, "%s: file %s, size %d, compress %d, flags %x", archname, name, hdr.uncompressedsize, hdr.compression, hdr.flags); +#endif + + src += hdr.namelength + hdr.extralength + hdr.commentlength; + } + delete[] buf; + + return files.length() > 0; +} + +static bool readlocalfileheader(FILE *f, ziplocalfileheader &h, uint offset) +{ + uchar buf[ZIP_LOCAL_FILE_SIZE]; + if(fseek(f, offset, SEEK_SET) < 0 || fread(buf, 1, ZIP_LOCAL_FILE_SIZE, f) != ZIP_LOCAL_FILE_SIZE) + return false; + uchar *src = buf; + h.signature = lilswap(*(uint *)src); src += 4; + h.version = lilswap(*(ushort *)src); src += 2; + h.flags = lilswap(*(ushort *)src); src += 2; + h.compression = lilswap(*(ushort *)src); src += 2; + h.modtime = lilswap(*(ushort *)src); src += 2; + h.moddate = lilswap(*(ushort *)src); src += 2; + h.crc32 = lilswap(*(uint *)src); src += 4; + h.compressedsize = lilswap(*(uint *)src); src += 4; + h.uncompressedsize = lilswap(*(uint *)src); src += 4; + h.namelength = lilswap(*(ushort *)src); src += 2; + h.extralength = lilswap(*(ushort *)src); src += 2; + if(h.signature != ZIP_LOCAL_FILE_SIGNATURE) return false; + // h.uncompressedsize or h.compressedsize may be zero - so don't validate + return true; +} + +static vector<ziparchive *> archives; + +ziparchive *findzip(const char *name) +{ + loopv(archives) if(!strcmp(name, archives[i]->name)) return archives[i]; + return NULL; +} + +static bool checkprefix(vector<zipfile> &files, const char *prefix, int prefixlen) +{ + loopv(files) + { + if(!strncmp(files[i].name, prefix, prefixlen)) return false; + } + return true; +} + +static void mountzip(ziparchive &arch, vector<zipfile> &files, const char *mountdir, const char *stripdir) +{ + string packagesdir = "packages/"; + path(packagesdir); + size_t striplen = stripdir ? strlen(stripdir) : 0; + if(!mountdir && !stripdir) loopv(files) + { + zipfile &f = files[i]; + const char *foundpackages = strstr(f.name, packagesdir); + if(foundpackages) + { + if(foundpackages > f.name) + { + stripdir = f.name; + striplen = foundpackages - f.name; + } + break; + } + const char *foundogz = strstr(f.name, ".ogz"); + if(foundogz) + { + const char *ogzdir = foundogz; + while(--ogzdir >= f.name && *ogzdir != PATHDIV); + if(ogzdir < f.name || checkprefix(files, f.name, ogzdir + 1 - f.name)) + { + if(ogzdir >= f.name) + { + stripdir = f.name; + striplen = ogzdir + 1 - f.name; + } + if(!mountdir) mountdir = "packages/base/"; + break; + } + } + } + string mdir = "", fname; + if(mountdir) + { + copystring(mdir, mountdir); + if(fixpackagedir(mdir) <= 1) mdir[0] = '\0'; + } + loopv(files) + { + zipfile &f = files[i]; + formatstring(fname, "%s%s", mdir, striplen && !strncmp(f.name, stripdir, striplen) ? &f.name[striplen] : f.name); + if(arch.files.access(fname)) continue; + char *mname = newstring(fname); + zipfile &mf = arch.files[mname]; + mf = f; + mf.name = mname; + } +} + +bool addzip(const char *name, const char *mount = NULL, const char *strip = NULL) +{ + string pname; + copystring(pname, name); + path(pname); + size_t plen = strlen(pname); + if(plen < 4 || !strchr(&pname[plen-4], '.')) concatstring(pname, ".zip"); + + ziparchive *exists = findzip(pname); + if(exists) + { + conoutf(CON_ERROR, "already added zip %s", pname); + return true; + } + + FILE *f = fopen(findfile(pname, "rb"), "rb"); + if(!f) + { + conoutf(CON_ERROR, "could not open file %s", pname); + return false; + } + zipdirectoryheader h; + vector<zipfile> files; + if(!findzipdirectory(f, h) || !readzipdirectory(pname, f, h.entries, h.offset, h.size, files)) + { + conoutf(CON_ERROR, "could not read directory in zip %s", pname); + fclose(f); + return false; + } + + ziparchive *arch = new ziparchive; + arch->name = newstring(pname); + arch->data = f; + mountzip(*arch, files, mount, strip); + archives.add(arch); + + conoutf("added zip %s", pname); + return true; +} + +bool removezip(const char *name) +{ + string pname; + copystring(pname, name); + path(pname); + int plen = (int)strlen(pname); + if(plen < 4 || !strchr(&pname[plen-4], '.')) concatstring(pname, ".zip"); + ziparchive *exists = findzip(pname); + if(!exists) + { + conoutf(CON_ERROR, "zip %s is not loaded", pname); + return false; + } + if(exists->openfiles) + { + conoutf(CON_ERROR, "zip %s has open files", pname); + return false; + } + conoutf("removed zip %s", exists->name); + archives.removeobj(exists); + delete exists; + return true; +} + +struct zipstream : stream +{ + enum + { + BUFSIZE = 16384 + }; + + ziparchive *arch; + zipfile *info; + z_stream zfile; + uchar *buf; + uint reading; + bool ended; + + zipstream() : arch(NULL), info(NULL), buf(NULL), reading(~0U), ended(false) + { + zfile.zalloc = NULL; + zfile.zfree = NULL; + zfile.opaque = NULL; + zfile.next_in = zfile.next_out = NULL; + zfile.avail_in = zfile.avail_out = 0; + } + + ~zipstream() + { + close(); + } + + void readbuf(uint size = BUFSIZE) + { + if(!zfile.avail_in) zfile.next_in = (Bytef *)buf; + size = min(size, uint(&buf[BUFSIZE] - &zfile.next_in[zfile.avail_in])); + if(arch->owner != this) + { + arch->owner = NULL; + if(fseek(arch->data, reading, SEEK_SET) >= 0) arch->owner = this; + else return; + } + uint remaining = info->offset + info->compressedsize - reading, + n = arch->owner == this ? fread(zfile.next_in + zfile.avail_in, 1, min(size, remaining), arch->data) : 0U; + zfile.avail_in += n; + reading += n; + } + + bool open(ziparchive *a, zipfile *f) + { + if(f->offset == ~0U) + { + ziplocalfileheader h; + a->owner = NULL; + if(!readlocalfileheader(a->data, h, f->header)) return false; + f->offset = f->header + ZIP_LOCAL_FILE_SIZE + h.namelength + h.extralength; + } + + if(f->compressedsize && inflateInit2(&zfile, -MAX_WBITS) != Z_OK) return false; + + a->openfiles++; + arch = a; + info = f; + reading = f->offset; + ended = false; + if(f->compressedsize) buf = new uchar[BUFSIZE]; + return true; + } + + void stopreading() + { + if(reading == ~0U) return; +#ifndef STANDALONE + if(dbgzip) conoutf(CON_DEBUG, info->compressedsize ? "%s: zfile.total_out %u, info->size %u" : "%s: reading %u, info->size %u", info->name, info->compressedsize ? uint(zfile.total_out) : reading - info->offset, info->size); +#endif + if(info->compressedsize) inflateEnd(&zfile); + reading = ~0U; + } + + void close() + { + stopreading(); + DELETEA(buf); + if(arch) { arch->owner = NULL; arch->openfiles--; arch = NULL; } + } + + offset size() { return info->size; } + bool end() { return reading == ~0U || ended; } + offset tell() { return reading != ~0U ? (info->compressedsize ? zfile.total_out : reading - info->offset) : offset(-1); } + + bool seek(offset pos, int whence) + { + if(reading == ~0U) return false; + if(!info->compressedsize) + { + switch(whence) + { + case SEEK_END: pos += info->offset + info->size; break; + case SEEK_CUR: pos += reading; break; + case SEEK_SET: pos += info->offset; break; + default: return false; + } + pos = clamp(pos, offset(info->offset), offset(info->offset + info->size)); + arch->owner = NULL; + if(fseek(arch->data, int(pos), SEEK_SET) < 0) return false; + arch->owner = this; + reading = pos; + ended = false; + return true; + } + + switch(whence) + { + case SEEK_END: pos += info->size; break; + case SEEK_CUR: pos += zfile.total_out; break; + case SEEK_SET: break; + default: return false; + } + + if(pos >= (offset)info->size) + { + reading = info->offset + info->compressedsize; + zfile.next_in += zfile.avail_in; + zfile.avail_in = 0; + zfile.total_in = info->compressedsize; + zfile.total_out = info->size; + arch->owner = NULL; + ended = false; + return true; + } + + if(pos < 0) return false; + if(pos >= (offset)zfile.total_out) pos -= zfile.total_out; + else + { + if(zfile.next_in && zfile.total_in <= uint(zfile.next_in - buf)) + { + zfile.avail_in += zfile.total_in; + zfile.next_in -= zfile.total_in; + } + else + { + arch->owner = NULL; + zfile.avail_in = 0; + zfile.next_in = NULL; + reading = info->offset; + } + inflateReset(&zfile); + } + + uchar skip[512]; + while(pos > 0) + { + size_t skipped = (size_t)min(pos, (offset)sizeof(skip)); + if(read(skip, skipped) != skipped) return false; + pos -= skipped; + } + + ended = false; + return true; + } + + size_t read(void *buf, size_t len) + { + if(reading == ~0U || !buf || !len) return 0; + if(!info->compressedsize) + { + if(arch->owner != this) + { + arch->owner = NULL; + if(fseek(arch->data, reading, SEEK_SET) < 0) { stopreading(); return 0; } + arch->owner = this; + } + + size_t n = fread(buf, 1, min(len, size_t(info->size + info->offset - reading)), arch->data); + reading += n; + if(n < len) ended = true; + return n; + } + + zfile.next_out = (Bytef *)buf; + zfile.avail_out = len; + while(zfile.avail_out > 0) + { + if(!zfile.avail_in) readbuf(BUFSIZE); + int err = inflate(&zfile, Z_NO_FLUSH); + if(err != Z_OK) + { + if(err == Z_STREAM_END) ended = true; + else + { +#ifndef STANDALONE + if(dbgzip) conoutf(CON_DEBUG, "inflate error: %s", zError(err)); +#endif + stopreading(); + } + break; + } + } + return len - zfile.avail_out; + } +}; + +stream *openzipfile(const char *name, const char *mode) +{ + for(; *mode; mode++) if(*mode=='w' || *mode=='a') return NULL; + loopvrev(archives) + { + ziparchive *arch = archives[i]; + zipfile *f = arch->files.access(name); + if(!f) continue; + zipstream *s = new zipstream; + if(s->open(arch, f)) return s; + delete s; + } + return NULL; +} + +bool findzipfile(const char *name) +{ + loopvrev(archives) + { + ziparchive *arch = archives[i]; + if(arch->files.access(name)) return true; + } + return false; +} + +int listzipfiles(const char *dir, const char *ext, vector<char *> &files) +{ + size_t extsize = ext ? strlen(ext)+1 : 0, dirsize = strlen(dir); + int dirs = 0; + loopvrev(archives) + { + ziparchive *arch = archives[i]; + int oldsize = files.length(); + enumerate(arch->files, zipfile, f, + { + if(strncmp(f.name, dir, dirsize)) continue; + const char *name = f.name + dirsize; + if(name[0] == PATHDIV) name++; + if(strchr(name, PATHDIV)) continue; + if(!ext) files.add(newstring(name)); + else + { + size_t namelen = strlen(name); + if(namelen > extsize) + { + namelen -= extsize; + if(name[namelen] == '.' && strncmp(name+namelen+1, ext, extsize-1)==0) + files.add(newstring(name, namelen)); + } + } + }); + if(files.length() > oldsize) dirs++; + } + return dirs; +} + +#ifndef STANDALONE +ICOMMAND(addzip, "sss", (const char *name, const char *mount, const char *strip), addzip(name, mount[0] ? mount : NULL, strip[0] ? strip : NULL)); +ICOMMAND(removezip, "s", (const char *name), removezip(name)); +#endif + |
