summaryrefslogtreecommitdiff
path: root/src/shared
diff options
context:
space:
mode:
authorxolatile2025-07-16 23:07:43 +0200
committerxolatile2025-07-16 23:07:43 +0200
commit7256502afa0babe60fcafbd2888cd3e33c3f9b6b (patch)
tree8a8495662a69bdadc4b5d9152656b9f02a44d668 /src/shared
parentbc596ac9d4cdd00abf537b88d3c544be161330cc (diff)
downloadxolatile-badassbug-7256502afa0babe60fcafbd2888cd3e33c3f9b6b.tar.xz
xolatile-badassbug-7256502afa0babe60fcafbd2888cd3e33c3f9b6b.tar.zst
Source code, broken...
Diffstat (limited to 'src/shared')
-rw-r--r--src/shared/command.h335
-rw-r--r--src/shared/crypto.cpp944
-rw-r--r--src/shared/cube.h68
-rw-r--r--src/shared/cube2font.c556
-rw-r--r--src/shared/ents.h237
-rw-r--r--src/shared/geom.cpp257
-rw-r--r--src/shared/geom.h1828
-rw-r--r--src/shared/glemu.cpp355
-rw-r--r--src/shared/glemu.h180
-rw-r--r--src/shared/glexts.h488
-rw-r--r--src/shared/iengine.h583
-rw-r--r--src/shared/igame.h130
-rw-r--r--src/shared/stream.cpp1264
-rw-r--r--src/shared/tools.cpp244
-rw-r--r--src/shared/tools.h1408
-rw-r--r--src/shared/zip.cpp588
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 &center, 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 &center, 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 &center, 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 &notfound)
+ {
+ HTFIND( , notfound);
+ }
+
+ template<class U>
+ const T &find(const U &key, const T &notfound)
+ {
+ 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
+