code:
/* For single inclusion into engine.c only! */

#pragma pack(push,1)
struct sectortypev5
{
    uint16_t wallptr, wallnum;
    int16_t ceilingpicnum, floorpicnum;
    int16_t ceilingheinum, floorheinum;
    int32_t ceilingz, floorz;
    int8_t ceilingshade, floorshade;
    char ceilingxpanning, floorxpanning;
    char ceilingypanning, floorypanning;
    char ceilingstat, floorstat;
    char ceilingpal, floorpal;
    char visibility;
    int16_t lotag, hitag;
    int16_t extra;
};
struct walltypev5
{
    int32_t x, y;
    int16_t point2;
    int16_t picnum, overpicnum;
    int8_t shade;
    int16_t cstat;
    char xrepeat, yrepeat, xpanning, ypanning;
    int16_t nextsector1, nextwall1;
    int16_t nextsector2, nextwall2;
    int16_t lotag, hitag;
    int16_t extra;
};
struct spritetypev5
{
    int32_t x, y, z;
    char cstat;
    int8_t shade;
    char xrepeat, yrepeat;
    int16_t picnum, ang, xvel, yvel, zvel, owner;
    int16_t sectnum, statnum;
    int16_t lotag, hitag;
    int16_t extra;
};
struct sectortypev6
{
    uint16_t wallptr, wallnum;
    int16_t ceilingpicnum, floorpicnum;
    int16_t ceilingheinum, floorheinum;
    int32_t ceilingz, floorz;
    int8_t ceilingshade, floorshade;
    char ceilingxpanning, floorxpanning;
    char ceilingypanning, floorypanning;
    char ceilingstat, floorstat;
    char ceilingpal, floorpal;
    char visibility;
    int16_t lotag, hitag, extra;
};
struct walltypev6
{
    int32_t x, y;
    int16_t point2, nextsector, nextwall;
    int16_t picnum, overpicnum;
    int8_t shade;
    char pal;
    int16_t cstat;
    char xrepeat, yrepeat, xpanning, ypanning;
    int16_t lotag, hitag, extra;
};
struct spritetypev6
{
    int32_t x, y, z;
    int16_t cstat;
    int8_t shade;
    char pal, clipdist;
    char xrepeat, yrepeat;
    int8_t xoffset, yoffset;
    int16_t picnum, ang, xvel, yvel, zvel, owner;
    int16_t sectnum, statnum;
    int16_t lotag, hitag, extra;
};
#pragma pack(pop)

static int16_t sectorofwallv5(int16_t theline)
{
    int16_t i, startwall, endwall, sucksect;

    sucksect = -1;
    for (i=0; i<numsectors; i++)
    {
        startwall = sector[i].wallptr;
        endwall = startwall + sector[i].wallnum - 1;
        if ((theline >= startwall) && (theline <= endwall))
        {
            sucksect = i;
            break;
        }
    }
    return(sucksect);
}

static void convertv5sectv6(struct sectortypev5 *from, struct sectortypev6 *to)
{
    to->wallptr = from->wallptr;
    to->wallnum = from->wallnum;
    to->ceilingpicnum = from->ceilingpicnum;
    to->floorpicnum = from->floorpicnum;
    to->ceilingheinum = from->ceilingheinum;
    to->floorheinum = from->floorheinum;
    to->ceilingz = from->ceilingz;
    to->floorz = from->floorz;
    to->ceilingshade = from->ceilingshade;
    to->floorshade = from->floorshade;
    to->ceilingxpanning = from->ceilingxpanning;
    to->floorxpanning = from->floorxpanning;
    to->ceilingypanning = from->ceilingypanning;
    to->floorypanning = from->floorypanning;
    to->ceilingstat = from->ceilingstat;
    to->floorstat = from->floorstat;
    to->ceilingpal = from->ceilingpal;
    to->floorpal = from->floorpal;
    to->visibility = from->visibility;
    to->lotag = from->lotag;
    to->hitag = from->hitag;
    to->extra = from->extra;
}

static void convertv5wallv6(struct walltypev5 *from, struct walltypev6 *to, int32_t i)
{
    to->x = from->x;
    to->y = from->y;
    to->point2 = from->point2;
    to->nextsector = from->nextsector1;
    to->nextwall = from->nextwall1;
    to->picnum = from->picnum;
    to->overpicnum = from->overpicnum;
    to->shade = from->shade;
    to->pal = sector[sectorofwallv5((int16_t)i)].floorpal;
    to->cstat = from->cstat;
    to->xrepeat = from->xrepeat;
    to->yrepeat = from->yrepeat;
    to->xpanning = from->xpanning;
    to->ypanning = from->ypanning;
    to->lotag = from->lotag;
    to->hitag = from->hitag;
    to->extra = from->extra;
}

static void convertv5sprv6(struct spritetypev5 *from, struct spritetypev6 *to)
{
    int16_t j;
    to->x = from->x;
    to->y = from->y;
    to->z = from->z;
    to->cstat = from->cstat;
    to->shade = from->shade;

    j = from->sectnum;
    if ((sector[j].ceilingstat&1) > 0)
        to->pal = sector[j].ceilingpal;
    else
        to->pal = sector[j].floorpal;

    to->clipdist = 32;
    to->xrepeat = from->xrepeat;
    to->yrepeat = from->yrepeat;
    to->xoffset = 0;
    to->yoffset = 0;
    to->picnum = from->picnum;
    to->ang = from->ang;
    to->xvel = from->xvel;
    to->yvel = from->yvel;
    to->zvel = from->zvel;
    to->owner = from->owner;
    to->sectnum = from->sectnum;
    to->statnum = from->statnum;
    to->lotag = from->lotag;
    to->hitag = from->hitag;
    to->extra = from->extra;
}

static void convertv6sectv7(struct sectortypev6 *from, sectortype *to)
{
    to->ceilingz = from->ceilingz;
    to->floorz = from->floorz;
    to->wallptr = from->wallptr;
    to->wallnum = from->wallnum;
    to->ceilingpicnum = from->ceilingpicnum;
    to->ceilingheinum = max(min(((int32_t)from->ceilingheinum)<<5,32767),-32768);
    if ((from->ceilingstat&2) == 0) to->ceilingheinum = 0;
    to->ceilingshade = from->ceilingshade;
    to->ceilingpal = from->ceilingpal;
    to->ceilingxpanning = from->ceilingxpanning;
    to->ceilingypanning = from->ceilingypanning;
    to->floorpicnum = from->floorpicnum;
    to->floorheinum = max(min(((int32_t)from->floorheinum)<<5,32767),-32768);
    if ((from->floorstat&2) == 0) to->floorheinum = 0;
    to->floorshade = from->floorshade;
    to->floorpal = from->floorpal;
    to->floorxpanning = from->floorxpanning;
    to->floorypanning = from->floorypanning;
    to->ceilingstat = from->ceilingstat;
    to->floorstat = from->floorstat;
    to->visibility = from->visibility;
    to->fogpal = 0;
    to->lotag = from->lotag;
    to->hitag = from->hitag;
    to->extra = from->extra;
#ifdef NEW_MAP_FORMAT
    to->ceilingbunch = to->floorbunch = -1;
#endif
}

static void convertv6wallv7(struct walltypev6 *from, walltype *to)
{
    to->x = from->x;
    to->y = from->y;
    to->point2 = from->point2;
    to->nextwall = from->nextwall;
    to->nextsector = from->nextsector;
    to->cstat = from->cstat;
    to->picnum = from->picnum;
    to->overpicnum = from->overpicnum;
    to->shade = from->shade;
    to->pal = from->pal;
    to->xrepeat = from->xrepeat;
    to->yrepeat = from->yrepeat;
    to->xpanning = from->xpanning;
    to->ypanning = from->ypanning;
    to->lotag = from->lotag;
    to->hitag = from->hitag;
    to->extra = from->extra;
#ifdef NEW_MAP_FORMAT
    to->upwall = to->dnwall = -1;
#endif
}

static void convertv6sprv7(struct spritetypev6 *from, spritetype *to)
{
    to->x = from->x;
    to->y = from->y;
    to->z = from->z;
    to->cstat = from->cstat;
    to->picnum = from->picnum;
    to->shade = from->shade;
    to->pal = from->pal;
    to->clipdist = from->clipdist;
    to->blend = 0;
    to->xrepeat = from->xrepeat;
    to->yrepeat = from->yrepeat;
    to->xoffset = from->xoffset;
    to->yoffset = from->yoffset;
    to->sectnum = from->sectnum;
    to->statnum = from->statnum;
    to->ang = from->ang;
    to->owner = from->owner;
    to->xvel = from->xvel;
    to->yvel = from->yvel;
    to->zvel = from->zvel;
    to->lotag = from->lotag;
    to->hitag = from->hitag;
    to->extra = from->extra;
}

repo_name: orochi/nblood

license: mit


code:
// "Build Engine & Tools" Copyright (c) 1993-1997 Ken Silverman
// Ken Silverman's official web site: "http://www.advsys.net/ken"
// See the included license file "BUILDLIC.TXT" for license info.
//
// This file has been modified from Ken Silverman's original release
// by Jonathon Fowler (jf@jonof.id.au)
// by the EDuke32 team (development@voidpoint.com)

#pragma once

#ifndef ENGINE_PRIV_H
#define ENGINE_PRIV_H

#define MAXPERMS 512
#define MAXARTFILES_BASE 200
#define MAXARTFILES_TOTAL 220
#define MAXCLIPDIST 1024

// Uncomment to clear the screen before each top-level draw (classic only).
// FIXME: doesn't work with mirrors.
//#define ENGINE_CLEAR_SCREEN

#ifdef YAX_ENABLE
# define YAX_MAXDRAWS 8
#endif

#if !defined(NOASM) && defined __cplusplus
extern "C" {
#endif
    extern intptr_t asm1, asm2, asm3, asm4;
    extern int32_t globalx1, globaly2;
#if !defined(NOASM) && defined __cplusplus
}
#endif

#ifdef __cplusplus
extern "C" {
#endif

extern uint16_t ATTRIBUTE((used)) sqrtable[4096], ATTRIBUTE((used)) shlookup[4096+256], ATTRIBUTE((used)) sqrtable_old[2048];

#if defined(_MSC_VER) && !defined(NOASM)

    //
    // Microsoft C Inline Assembly Routines
    //

    static inline int32_t nsqrtasm(int32_t a)
    {
        _asm
        {
            push ebx
            mov eax, a
            test eax, 0xff000000
            mov ebx, eax
            jnz short over24
            shr ebx, 12
            mov cx, word ptr shlookup[ebx*2]
            jmp short under24
            over24 :
            shr ebx, 24
                mov cx, word ptr shlookup[ebx*2+8192]
                under24 :
                shr eax, cl
                mov cl, ch
                mov ax, word ptr sqrtable[eax*2]
                shr eax, cl
                pop ebx
        }
    }

    static inline int32_t getclipmask(int32_t a, int32_t b, int32_t c, int32_t d)
    {
        _asm
        {
            push ebx
            mov eax, a
            mov ebx, b
            mov ecx, c
            mov edx, d
            sar eax, 31
            add ebx, ebx
            adc eax, eax
            add ecx, ecx
            adc eax, eax
            add edx, edx
            adc eax, eax
            mov ebx, eax
            shl ebx, 4
            or al, 0xf0
            xor eax, ebx
            pop ebx
        }
    }

#elif defined(__GNUC__) && defined(__i386__) && !defined(NOASM)	// _MSC_VER

    //
    // GCC "Inline" Assembly Routines
    //

#define nsqrtasm(a) \
    ({ int32_t __r, __a=(a); \
       __asm__ __volatile__ ( \
        "testl $0xff000000, %%eax\n\t" \
        "movl %%eax, %%ebx\n\t" \
        "jnz 0f\n\t" \
        "shrl $12, %%ebx\n\t" \
        "movw " ASMSYM("shlookup") "(,%%ebx,2), %%cx\n\t" \
        "jmp 1f\n\t" \
        "0:\n\t" \
        "shrl $24, %%ebx\n\t" \
        "movw (" ASMSYM("shlookup") "+8192)(,%%ebx,2), %%cx\n\t" \
        "1:\n\t" \
        "shrl %%cl, %%eax\n\t" \
        "movb %%ch, %%cl\n\t" \
        "movw " ASMSYM("sqrtable") "(,%%eax,2), %%ax\n\t" \
        "shrl %%cl, %%eax" \
        : "=a" (__r) : "a" (__a) : "ebx", "ecx", "cc"); \
     __r; })

#define getclipmask(a,b,c,d) \
    ({ int32_t __a=(a), __b=(b), __c=(c), __d=(d); \
       __asm__ __volatile__ ("sarl $31, %%eax; addl %%ebx, %%ebx; adcl %%eax, %%eax; " \
                "addl %%ecx, %%ecx; adcl %%eax, %%eax; addl %%edx, %%edx; " \
                "adcl %%eax, %%eax; movl %%eax, %%ebx; shl $4, %%ebx; " \
                "orb $0xf0, %%al; xorl %%ebx, %%eax" \
        : "=a" (__a), "=b" (__b), "=c" (__c), "=d" (__d) \
        : "a" (__a), "b" (__b), "c" (__c), "d" (__d) : "cc"); \
     __a; })

#else   // __GNUC__ && __i386__

    static FORCE_INLINE int32_t nsqrtasm(uint32_t a)
    {
        // JBF 20030901: This was a damn lot simpler to reverse engineer than
        // msqrtasm was. Really, it was just like simplifying an algebra equation.
        uint16_t const c = shlookup[(a & 0xff000000) ? ((a >> 24) + 4096):(a>>12)];
        a >>= c&0xff;
        return ((a&0xffff0000)|(sqrtable[a])) >> ((c&0xff00) >> 8);
    }

    static FORCE_INLINE int32_t getclipmask(int32_t a, int32_t b, int32_t c, int32_t d)
    {
        // Ken did this
        d = ((a<0)<<3) + ((b<0)<<2) + ((c<0)<<1) + (d<0);
        return (((d<<4)^0xf0)|d);
    }

#endif

static FORCE_INLINE int32_t ksqrt_inline(uint32_t n)
{
    if (enginecompatibilitymode == ENGINE_19950829)
        return ksqrtasm_old(n);
    return nsqrtasm(n);
}

extern int16_t thesector[MAXWALLSB], thewall[MAXWALLSB];
extern int16_t bunchfirst[MAXWALLSB], bunchlast[MAXWALLSB];
extern int16_t maskwall[MAXWALLSB], maskwallcnt;
extern tspriteptr_t tspriteptr[MAXSPRITESONSCREEN + 1];
extern uint8_t* mirrorBuffer;
extern int32_t xdimen, xdimenrecip, halfxdimen, xdimenscale, xdimscale, ydimen;
extern float fxdimen;
extern intptr_t frameoffset;
extern int32_t globalposx, globalposy, globalposz, globalhoriz;
extern fix16_t qglobalhoriz, qglobalang;
extern float fglobalposx, fglobalposy, fglobalposz;
extern int16_t globalang, globalcursectnum;
extern int32_t globalpal, cosglobalang, singlobalang;
extern int32_t cosviewingrangeglobalang, sinviewingrangeglobalang;
extern int32_t globalhisibility, globalpisibility, globalcisibility;
#ifdef USE_OPENGL
extern int32_t globvis2, globalvisibility2, globalhisibility2, globalpisibility2, globalcisibility2;
#endif
extern int32_t globvis, globalvisibility;
extern int32_t xyaspect;
extern int32_t globalshade;
extern int16_t globalpicnum;

extern int32_t globalorientation;

extern int16_t editstatus;

extern int16_t searchit;
extern int32_t searchx, searchy;
extern int16_t searchsector, searchwall, searchstat;
extern int16_t searchbottomwall, searchisbottom;

extern char inpreparemirror;

extern uint8_t tilefilenum[MAXTILES];
extern char picsiz[MAXTILES];
extern int16_t sectorborder[256];
extern int32_t qsetmode;
extern int32_t hitallsprites;

extern int32_t xb1[MAXWALLSB];
extern int32_t rx1[MAXWALLSB], ry1[MAXWALLSB];
extern int16_t bunchp2[MAXWALLSB];
extern int16_t numscans, numbunches;
extern int32_t rxi[8], ryi[8];

extern int16_t wallsect[MAXWALLS];

#ifdef USE_OPENGL

// For GL_EXP2 fog:
#define FOGSCALE 0.0000768f

void calc_and_apply_fog(int32_t shade, int32_t vis, int32_t pal);
void calc_and_apply_fog_factor(int32_t shade, int32_t vis, int32_t pal, float factor);
#endif

// int32_t wallmost(int16_t *mostbuf, int32_t w, int32_t sectnum, char dastat);
int32_t wallfront(int32_t l1, int32_t l2);

void set_globalang(fix16_t const ang);

int32_t animateoffs(int tilenum, int fakevar);

static FORCE_INLINE int32_t bad_tspr(tspriteptr_t tspr)
{
    // NOTE: tspr->owner >= MAXSPRITES (could be model) has to be handled by
    // caller.
    return (tspr->owner < 0 || (unsigned)tspr->picnum >= MAXTILES);
}

//
// getpalookup (internal)
//
static FORCE_INLINE int32_t getpalookup(int32_t davis, int32_t dashade)
{
    if (getpalookup_replace)
        return getpalookup_replace(davis, dashade);
    return min(max(dashade + (davis >> 8), 0), numshades - 1);
}

static FORCE_INLINE int32_t getpalookupsh(int32_t davis) { return getpalookup(davis, globalshade) << 8; }

void dorotspr_handle_bit2(int32_t *sx, int32_t *sy, int32_t *z, int32_t dastat,
                          int32_t cx1_plus_cx2, int32_t cy1_plus_cy2,
                          int32_t *ret_yxaspect, int32_t *ret_xyaspect);

////// yax'y stuff //////
#ifdef USE_OPENGL
extern void polymost_scansector(int32_t sectnum);
#endif
int32_t renderAddTsprite(int16_t z, int16_t sectnum);
#ifdef YAX_ENABLE
extern int32_t g_nodraw, scansector_retfast, scansector_collectsprites;
extern int32_t yax_globallev, yax_globalbunch;
extern int32_t yax_globalcf, yax_nomaskpass, yax_nomaskdidit;
extern uint8_t haveymost[(YAX_MAXBUNCHES+7)>>3];
extern uint8_t yax_gotsector[(MAXSECTORS+7)>>3];
extern int32_t yax_polymostclearzbuffer;

static FORCE_INLINE int32_t yax_isislandwall(int32_t line, int32_t cf) { return (yax_vnextsec(line, cf) >= 0); }
#endif

#ifdef YAX_DEBUG
extern char m32_debugstr[64][128];
extern int32_t m32_numdebuglines;
# define yaxdebug(fmt, ...)  do { if (m32_numdebuglines<64) Bsnprintf(m32_debugstr[m32_numdebuglines++], 128, fmt, ##__VA_ARGS__); } while (0)
# define yaxprintf(fmt, ...) do { initprintf(fmt, ##__VA_ARGS__); } while (0)
#else
# define yaxdebug(fmt, ...)
# define yaxprintf(fmt, ...)
#endif

#if defined(_MSC_VER) && !defined(NOASM)

static inline void setgotpic(int32_t a)
{
    _asm {
        push ebx
        mov eax, a
        mov ebx, eax
        cmp byte ptr walock[eax], 200
        jae skipit
        mov byte ptr walock[eax], 199
skipit:
        shr eax, 3
        and ebx, 7
        mov dl, byte ptr gotpic[eax]
        mov bl, byte ptr pow2char[ebx]
        or dl, bl
        mov byte ptr gotpic[eax], dl
        pop ebx
    }
}

#elif defined(__GNUC__) && defined(__i386__) && !defined(NOASM)	// _MSC_VER

#define setgotpic(a) \
({ int32_t __a=(a); \
    __asm__ __volatile__ ( \
                   "movl %%eax, %%ebx\n\t" \
                   "cmpb $200, " ASMSYM("walock") "(%%eax)\n\t" \
                   "jae 0f\n\t" \
                   "movb $199, " ASMSYM("walock") "(%%eax)\n\t" \
                   "0:\n\t" \
                   "shrl $3, %%eax\n\t" \
                   "andl $7, %%ebx\n\t" \
                   "movb " ASMSYM("gotpic") "(%%eax), %%dl\n\t" \
                   "movb " ASMSYM("pow2char_") "(%%ebx), %%bl\n\t" \
                   "orb %%bl, %%dl\n\t" \
                   "movb %%dl, " ASMSYM("gotpic") "(%%eax)" \
                   : "=a" (__a) : "a" (__a) \
                   : "ebx", "edx", "memory", "cc"); \
                       __a; })

#else	// __GNUC__ && __i386__

static FORCE_INLINE void setgotpic(int32_t tilenume)
{
    if (walock[tilenume] < CACHE1D_LOCKED) walock[tilenume] = CACHE1D_UNLOCKED;
    gotpic[tilenume>>3] |= pow2char[tilenume&7];
}

#endif

// Get properties of parallaxed sky to draw.
// Returns: pointer to tile offset array. Sets-by-pointer the other three.
static FORCE_INLINE const int8_t *getpsky(int32_t picnum, int32_t *dapyscale, int32_t *dapskybits, int32_t *dapyoffs, int32_t *daptileyscale)
{
    psky_t const * const psky = &multipsky[getpskyidx(picnum)];

    if (dapskybits)
        *dapskybits = (pskybits_override == -1 ? psky->lognumtiles : pskybits_override);
    if (dapyscale)
        *dapyscale = (parallaxyscale_override == 0 ? psky->horizfrac : parallaxyscale_override);
    if (dapyoffs)
        *dapyoffs = psky->yoffs + parallaxyoffs_override;
    if (daptileyscale)
        *daptileyscale = psky->yscale;

    return psky->tileofs;
}

static FORCE_INLINE void set_globalpos(int32_t const x, int32_t const y, int32_t const z)
{
    globalposx = x, fglobalposx = (float)x;
    globalposy = y, fglobalposy = (float)y;
    globalposz = z, fglobalposz = (float)z;
}

#ifdef __cplusplus
}
#endif

template <typename T> static FORCE_INLINE void tileUpdatePicnum(T * const tileptr, int const obj)
{
    auto &tile = *tileptr;

    if (picanm[tile].sf & PICANM_ANIMTYPE_MASK)
        tile += animateoffs(tile, obj);

    if (((obj & 16384) == 16384) && (globalorientation & CSTAT_WALL_ROTATE_90) && rottile[tile].newtile != -1)
        tile = rottile[tile].newtile;
}

// x1, y1: in/out
// rest x/y: out
static inline void get_wallspr_points(void const * const ptr, int32_t *x1, int32_t *x2, int32_t *y1, int32_t *y2)
{
    //These lines get the 2 points of the rotated sprite
    //Given: (x1, y1) starts out as the center point
    auto spr = (uspriteptr_t)ptr;
    const int32_t tilenum=spr->picnum, ang=spr->ang;
    const int32_t xrepeat = spr->xrepeat;
    int32_t xoff = picanm[tilenum].xofs + spr->xoffset;
    int32_t k, l, dax, day;

    if (spr->cstat&4)
        xoff = -xoff;

    dax = sintable[ang&2047]*xrepeat;
    day = sintable[(ang+1536)&2047]*xrepeat;

    l = tilesiz[tilenum].x;
    k = (l>>1)+xoff;

    *x1 -= mulscale16(dax,k);
    *x2 = *x1 + mulscale16(dax,l);

    *y1 -= mulscale16(day,k);
    *y2 = *y1 + mulscale16(day,l);
}

// x1, y1: in/out
// rest x/y: out
static inline void get_floorspr_points(void const * const ptr, int32_t px, int32_t py,
                                int32_t *x1, int32_t *x2, int32_t *x3, int32_t *x4,
                                int32_t *y1, int32_t *y2, int32_t *y3, int32_t *y4,
                                int32_t const heinum)
{
    auto spr = (uspriteptr_t)ptr;
    const int32_t tilenum = spr->picnum;
    const int32_t cosang = sintable[(spr->ang+512)&2047];
    const int32_t sinang = sintable[spr->ang&2047];

    vec2_t const span = { tilesiz[tilenum].x, tilesiz[tilenum].y};
    vec2_t const repeat = { spr->xrepeat, spr->yrepeat };

    vec2_t adjofs = { picanm[tilenum].xofs, picanm[tilenum].yofs };

    int32_t const ratio = nsqrtasm(heinum*heinum+16777216);

    if (heinum == 0)
    {
        adjofs.x += spr->xoffset;
        adjofs.y += spr->yoffset;
    }

    if (spr->cstat & 4)
        adjofs.x = -adjofs.x;

    if (spr->cstat & 8)
        adjofs.y = -adjofs.y;

    vec2_t const center = { ((span.x >> 1) + adjofs.x) * repeat.x, ((span.y >> 1) + adjofs.y) * repeat.y };
    vec2_t const rspan  = { span.x * repeat.x, span.y * repeat.y };
    vec2_t const ofs    = { -divscale12(mulscale16(cosang, rspan.y), ratio), -divscale12(mulscale16(sinang, rspan.y), ratio) };
    vec2_t const cossinslope = { divscale12(cosang, ratio), divscale12(sinang, ratio) };

    *x1 += dmulscale16(sinang, center.x, cossinslope.x, center.y) - px;
    *y1 += dmulscale16(cossinslope.y, center.y, -cosang, center.x) - py;

    *x2 = *x1 - mulscale16(sinang, rspan.x);
    *y2 = *y1 + mulscale16(cosang, rspan.x);

    *x3 = *x2 + ofs.x, *x4 = *x1 + ofs.x;
    *y3 = *y2 + ofs.y, *y4 = *y1 + ofs.y;
}

#endif	/* ENGINE_PRIV_H */


code:
#include "fix16.h"

#include "pragmas.h"
#include "fix16_int64.h"

/* Subtraction and addition with overflow detection.
 * The versions without overflow detection are inlined in the header.
 */
FIXMATH_FUNC_ATTRS fix16_t fix16_add(fix16_t a, fix16_t b)
{
    // Use unsigned integers because overflow with signed integers is
    // an undefined operation (http://www.airs.com/blog/archives/120).
    uint32_t _a = a, _b = b;
    uint32_t sum = _a + _b;

    // Overflow can only happen if sign of a == sign of b, and then
    // it causes sign of sum != sign of a.
    if (!((_a ^ _b) & 0x80000000) && ((_a ^ sum) & 0x80000000))
        return FIX16_OVERFLOW;

    return sum;
}

FIXMATH_FUNC_ATTRS fix16_t fix16_sub(fix16_t a, fix16_t b)
{
    uint32_t _a = a, _b = b;
    uint32_t diff = _a - _b;

    // Overflow can only happen if sign of a != sign of b, and then
    // it causes sign of diff != sign of a.
    if (((_a ^ _b) & 0x80000000) && ((_a ^ diff) & 0x80000000))
        return FIX16_OVERFLOW;

    return diff;
}

/* Saturating arithmetic */
FIXMATH_FUNC_ATTRS fix16_t fix16_sadd(fix16_t a, fix16_t b)
{
    fix16_t result = fix16_add(a, b);

    if (result == FIX16_OVERFLOW)
        return (a >= 0) ? FIX16_MAX : FIX16_MIN;

    return result;
}

FIXMATH_FUNC_ATTRS fix16_t fix16_ssub(fix16_t a, fix16_t b)
{
    fix16_t result = fix16_sub(a, b);

    if (result == FIX16_OVERFLOW)
        return (a >= 0) ? FIX16_MAX : FIX16_MIN;

    return result;
}

/* 64-bit implementation for fix16_mul. Fastest version for e.g. ARM Cortex M3.
 * Performs a 32*32 -> 64bit multiplication. The middle 32 bits are the result,
 * bottom 16 bits are used for rounding, and upper 16 bits are used for overflow
 * detection.
 */

FIXMATH_FUNC_ATTRS fix16_t fix16_mul(fix16_t inArg0, fix16_t inArg1)
{
    int64_t product = (int64_t)inArg0 * inArg1;

    // The upper 17 bits should all be the same (the sign).
    uint32_t upper = (product >> 47);

    if (product < 0)
    {
        if (~upper)
                return FIX16_OVERFLOW;

        // This adjustment is required in order to round -1/2 correctly
        product--;
    }
    else
    {
        if (upper)
                return FIX16_OVERFLOW;
    }

    fix16_t result = product >> 16;
    result += (product & 0x8000) >> 15;

    return result;
}

/* Wrapper around fix16_mul to add saturating arithmetic. */
FIXMATH_FUNC_ATTRS fix16_t fix16_smul(fix16_t inArg0, fix16_t inArg1)
{
    fix16_t result = fix16_mul(inArg0, inArg1);

    if (result == FIX16_OVERFLOW)
    {
        if ((inArg0 >= 0) == (inArg1 >= 0))
            return FIX16_MAX;
        else
            return FIX16_MIN;
    }

    return result;
}

/* 32-bit implementation of fix16_div. Fastest version for e.g. ARM Cortex M3.
 * Performs 32-bit divisions repeatedly to reduce the remainder. For this to
 * be efficient, the processor has to have 32-bit hardware division.
 */
#ifdef __GNUC__
// Count leading zeros, using processor-specific instruction if available.
#define clz(x) (__builtin_clzl(x) - (8 * sizeof(long) - 32))
#else
static uint8_t clz(uint32_t x)
{
    uint8_t result = 0;
    if (x == 0) return 32;
    while (!(x & 0xF0000000)) { result += 4; x <<= 4; }
    while (!(x & 0x80000000)) { result += 1; x <<= 1; }
    return result;
}
#endif

FIXMATH_FUNC_ATTRS fix16_t fix16_div(fix16_t a, fix16_t b)
{
    // This uses a hardware 32/32 bit division multiple times, until we have
    // computed all the bits in (a<<17)/b. Usually this takes 1-3 iterations.

    if (b == 0)
            return FIX16_MIN;

    uint32_t remainder = (a >= 0) ? a : (-a);
    uint32_t divider = (b >= 0) ? b : (-b);
    uint32_t quotient = 0;
    int bit_pos = 17;

    // Kick-start the division a bit.
    // This improves speed in the worst-case scenarios where N and D are large
    // It gets a lower estimate for the result by N/(D >> 17 + 1).
    if (divider & 0xFFF00000)
    {
        uint32_t shifted_div = ((divider >> 17) + 1);
        quotient = divideu32(remainder, shifted_div);
        remainder -= ((uint64_t)quotient * divider) >> 17;
    }

    // If the divider is divisible by 2^n, take advantage of it.
    while (!(divider & 0xF) && bit_pos >= 4)
    {
        divider >>= 4;
        bit_pos -= 4;
    }

    while (remainder && bit_pos >= 0)
    {
        // Shift remainder as much as we can without overflowing
        int shift = clz(remainder);
        if (shift > bit_pos) shift = bit_pos;
        remainder <<= shift;
        bit_pos -= shift;

        uint32_t div = divideu32(remainder, divider);
        remainder = remainder % divider;
        quotient += div << bit_pos;

        if (div & ~(0xFFFFFFFF >> bit_pos))
                return FIX16_OVERFLOW;

        remainder <<= 1;
        bit_pos--;
    }

    // Quotient is always positive so rounding is easy
    quotient++;

    fix16_t result = quotient >> 1;

    // Figure out the sign of the result
    if ((a ^ b) & 0x80000000)
    {
        if (result == FIX16_MIN)
                return FIX16_OVERFLOW;

        result = -result;
    }

    return result;
}

/* Wrapper around fix16_div to add saturating arithmetic. */
FIXMATH_FUNC_ATTRS fix16_t fix16_sdiv(fix16_t inArg0, fix16_t inArg1)
{
    fix16_t result = fix16_div(inArg0, inArg1);

    if (result == FIX16_OVERFLOW)
    {
        if ((inArg0 >= 0) == (inArg1 >= 0))
            return FIX16_MAX;
        else
            return FIX16_MIN;
    }

    return result;
}

FIXMATH_FUNC_ATTRS fix16_t fix16_lerp8(fix16_t inArg0, fix16_t inArg1, uint8_t inFract)
{
    int64_t tempOut = int64_mul_i32_i32(inArg0, ((1 << 8) - inFract));
    tempOut = int64_add(tempOut, int64_mul_i32_i32(inArg1, inFract));
    tempOut = int64_shift(tempOut, -8);
    return (fix16_t)int64_lo(tempOut);
}

FIXMATH_FUNC_ATTRS fix16_t fix16_lerp16(fix16_t inArg0, fix16_t inArg1, uint16_t inFract)
{
    int64_t tempOut = int64_mul_i32_i32(inArg0, (((int32_t)1 << 16) - inFract));
    tempOut = int64_add(tempOut, int64_mul_i32_i32(inArg1, inFract));
    tempOut = int64_shift(tempOut, -16);
    return (fix16_t)int64_lo(tempOut);
}

FIXMATH_FUNC_ATTRS fix16_t fix16_lerp32(fix16_t inArg0, fix16_t inArg1, uint32_t inFract)
{
    int64_t tempOut;
    tempOut  = ((int64_t)inArg0 * (0 - inFract));
    tempOut	+= ((int64_t)inArg1 * inFract);
    tempOut >>= 32;
    return (fix16_t)tempOut;
}

static const uint32_t scales[8] = {
    /* 5 decimals is enough for full fix16_t precision */
    1, 10, 100, 1000, 10000, 100000, 100000, 100000
};

static char *itoa_loop(char *buf, uint32_t scale, uint32_t value, bool skip)
{
    while (scale)
    {
        unsigned digit = (value / scale);

        if (!skip || digit || scale == 1)
        {
            skip = false;
            *buf++ = '0' + digit;
            value %= scale;
        }

        scale /= 10;
    }
    return buf;
}

void fix16_to_str(fix16_t value, char *buf, int decimals)
{
    uint32_t uvalue = (value >= 0) ? value : -value;
    if (value < 0)
        *buf++ = '-';

    /* Separate the integer and decimal parts of the value */
    unsigned intpart = uvalue >> 16;
    uint32_t fracpart = uvalue & 0xFFFF;
    uint32_t scale = scales[decimals & 7];
    fracpart = fix16_mul(fracpart, scale);

    if (fracpart >= scale)
    {
        /* Handle carry from decimal part */
        intpart++;
        fracpart -= scale;
    }

    /* Format integer part */
    buf = itoa_loop(buf, 10000, intpart, true);

    /* Format decimal part (if any) */
    if (scale != 1)
    {
        *buf++ = '.';
        buf = itoa_loop(buf, scale / 10, fracpart, false);
    }

    *buf = '\0';
}

fix16_t fix16_from_str(const char *buf)
{
    while (isspace(*buf))
        buf++;

    /* Decode the sign */
    bool negative = (*buf == '-');
    if (*buf == '+' || *buf == '-')
        buf++;

    /* Decode the integer part */
    uint32_t intpart = 0;
    int count = 0;
    while (isdigit(*buf))
    {
        intpart *= 10;
        intpart += *buf++ - '0';
        count++;
    }

    if (count == 0 || count > 5
        || intpart > 32768 || (!negative && intpart > 32767))
        return FIX16_OVERFLOW;

    fix16_t value = intpart << 16;

    /* Decode the decimal part */
    if (*buf == '.' || *buf == ',')
    {
        buf++;

        uint32_t fracpart = 0;
        uint32_t scale = 1;
        while (isdigit(*buf) && scale < 100000)
        {
            scale *= 10;
            fracpart *= 10;
            fracpart += *buf++ - '0';
        }

        value += fix16_div(fracpart, scale);
    }

    /* Verify that there is no garbage left over */
    while (*buf != '\0')
    {
        if (!isdigit(*buf) && !isspace(*buf))
            return FIX16_OVERFLOW;

        buf++;
    }

    return negative ? -value : value;
}


code:
/*
 * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
 * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice including the dates of first publication and
 * either this permission notice or a reference to
 * http://oss.sgi.com/projects/FreeB/
 * shall be included in all copies or substantial portions of the Software.
 */

#include "glbuild.h"

#include "baselayer.h"
#include "build.h"

#if defined USE_OPENGL
# ifdef RENDERTYPESDL
#  include "sdlayer.h"
# endif
# if !defined _WIN32
#  include <dlfcn.h>
# endif

BuildGLState gl;
GLuint samplerObjectIDs[NUM_SAMPLERS];

void buildgl_outputDebugMessage(uint8_t severity, const char* format, ...)
{
    if (!glinfo.debugoutput || r_polymostDebug < severity)
        return;

    size_t size = max(PRINTF_INITIAL_BUFFER_SIZE >> 1, nextPow2(Bstrlen(format)));
    char *buf = nullptr;
    int len;

    do
    {
        va_list va;
        buf = (char *)Xrealloc(buf, (size <<= 1));
        va_start(va, format);
        len = Bvsnprintf(buf, size, format, va);
        va_end(va);
    } while ((unsigned)len >= size);

    glDebugMessageInsertARB(GL_DEBUG_SOURCE_APPLICATION_ARB,
                            GL_DEBUG_TYPE_OTHER_ARB,
                            0,
                            GL_DEBUG_SEVERITY_HIGH_ARB+severity-1,
                            -1,
                            buf);
    Xfree(buf);
}

void buildgl_resetStateAccounting()
{
    for (auto i=GL_TEXTURE0;i<MAXTEXUNIT;i++)
        inthash_free(&gl.state[TEXUNIT_INDEX_FROM_NAME(i)]);

    Bmemset(&gl, 0, sizeof(BuildGLState));

    for (auto i=GL_TEXTURE0;i<MAXTEXUNIT;i++)
    {
        buildgl_bindSamplerObject(TEXUNIT_INDEX_FROM_NAME(i), 0);
        gl.currentBoundSampler[TEXUNIT_INDEX_FROM_NAME(i)] = (glsamplertype)-1;
        gl.state[TEXUNIT_INDEX_FROM_NAME(i)].count = 64;
        inthash_init(&gl.state[TEXUNIT_INDEX_FROM_NAME(i)]);
    }

    gl.fullReset = 1;
}

void buildgl_setViewport(GLint x, GLint y, GLsizei width, GLsizei height)
{
    if (x == gl.x && y == gl.y && width == gl.width && height == gl.height)
        return;

    gl.x = x;
    gl.y = y;
    gl.width = width;
    gl.height = height;

    glViewport(x, y, width, height);
}

void buildgl_setDepthFunc(GLenum func)
{
    if ((GLenum)inthash_find(&gl.state[0], GL_DEPTH_FUNC) == func)
        return;

    glDepthFunc(func);
    inthash_add(&gl.state[0], GL_DEPTH_FUNC, func, 1);
}

void buildgl_setAlphaFunc(GLenum func, GLfloat ref)
{
    if ((GLenum)inthash_find(&gl.state[0], GL_ALPHA_TEST_FUNC) == func && inthash_find(&gl.state[0], GL_ALPHA_TEST_REF) == *(int32_t *)&ref)
        return;

    glAlphaFunc(func, ref);
    inthash_add(&gl.state[0], GL_ALPHA_TEST_FUNC, func, 1);
    inthash_add(&gl.state[0], GL_ALPHA_TEST_REF, *(int32_t *)&ref, 1);
}

void buildgl_setEnabled(GLenum key)
{
    if (inthash_find(&gl.state[0], key) == GL_TRUE)
        return;

    glEnable(key);

    inthash_add(&gl.state[0], key, GL_TRUE, 1);
}

void buildgl_setDisabled(GLenum key)
{
    if (inthash_find(&gl.state[0], key) == GL_FALSE)
        return;

    glDisable(key);

    inthash_add(&gl.state[0], key, GL_FALSE, 1);
}

void buildgl_useShaderProgram(uint32_t shaderID)
{
    glUseProgram(shaderID);
    gl.currentShaderProgramID = shaderID;
}

//POGOTODO: these wrappers won't be needed down the line -- remove them once proper draw call organization is finished
void buildgl_activeTexture(GLenum texture)
{
    if (gl.currentActiveTexture != texture)
    {
        gl.currentActiveTexture = texture;
        glActiveTexture(texture);
    }
}

void buildgl_bindBuffer(GLenum target, uint32_t bufferID)
{
    if ((uint32_t)inthash_find(&gl.state[ACTIVETEX], target) == bufferID)
        return;

    glBindBuffer(target, bufferID);

    if (bufferID == 0)
        inthash_delete(&gl.state[ACTIVETEX], target);
    else
        inthash_add(&gl.state[ACTIVETEX], target, bufferID, 1);
}

//POGOTODO: replace this and buildgl_activeTexture with proper draw call organization
void buildgl_bindTexture(GLenum target, uint32_t textureID)
{
    if (/*textureID == 0 ||*/
        /*gl.currentActiveTexture != GL_TEXTURE0 ||*/
        (uint32_t)inthash_find(&gl.state[ACTIVETEX], target) != textureID /*||
        videoGetRenderMode() != REND_POLYMOST*/)
    {
        glBindTexture(target, textureID);
//        if (gl.currentActiveTexture == GL_TEXTURE0)
        {
            if (textureID == 0)
                inthash_delete(&gl.state[ACTIVETEX], target);
            else
                inthash_add(&gl.state[ACTIVETEX], target, textureID, 1);
        }
    }
}

void buildgl_resetSamplerObjects(void)
{
    glanisotropy    = clamp<int>(glanisotropy, 1, glinfo.maxanisotropy);
    gltexfiltermode = clamp(gltexfiltermode, 0, NUMGLFILTERMODES-1);

    auto &f = glfiltermodes[gltexfiltermode];

    if (!glIsSampler(samplerObjectIDs[1]))
        glGenSamplers(NUM_SAMPLERS-1, &samplerObjectIDs[1]);

    // common properties
    for (int i = 1; i < ARRAY_SSIZE(samplerObjectIDs); i++)
    {
        auto &s = samplerObjectIDs[i];

        glSamplerParameteri(s, GL_TEXTURE_MAG_FILTER, f.mag);
        glSamplerParameteri(s, GL_TEXTURE_MIN_FILTER, f.min);
        //glSamplerParameteri(s, GL_TEXTURE_BASE_LEVEL, 0);
        //glSamplerParameteri(s, GL_TEXTURE_MAX_LEVEL, 0);
        glSamplerParameterf(s, GL_TEXTURE_MAX_ANISOTROPY_EXT, polymost_getanisotropy());
    }

    GLuint s = samplerObjectIDs[SAMPLER_CLAMP];
    glSamplerParameteri(s, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glSamplerParameteri(s, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    s = samplerObjectIDs[SAMPLER_WRAP_T];
    glSamplerParameteri(s, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);

    s = samplerObjectIDs[SAMPLER_WRAP_S];
    glSamplerParameteri(s, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    s = samplerObjectIDs[SAMPLER_NEAREST_CLAMP];
    glSamplerParameteri(s, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glSamplerParameteri(s, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glSamplerParameteri(s, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glSamplerParameteri(s, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glSamplerParameteri(s, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1);

    s = samplerObjectIDs[SAMPLER_NEAREST_WRAP];
    glSamplerParameteri(s, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glSamplerParameteri(s, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glSamplerParameteri(s, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1);

    s = samplerObjectIDs[SAMPLER_LINEAR_CLAMP];
    glSamplerParameteri(s, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glSamplerParameteri(s, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    glSamplerParameteri(s, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glSamplerParameteri(s, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    s = samplerObjectIDs[SAMPLER_LINEAR_WRAP];
    glSamplerParameteri(s, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glSamplerParameteri(s, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);

    s = samplerObjectIDs[SAMPLER_NEAREST_NEAREST_CLAMP];
    glSamplerParameteri(s, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glSamplerParameteri(s, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
    glSamplerParameteri(s, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glSamplerParameteri(s, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glSamplerParameteri(s, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1);

    s = samplerObjectIDs[SAMPLER_NEAREST_NEAREST_WRAP];
    glSamplerParameteri(s, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glSamplerParameteri(s, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
    glSamplerParameteri(s, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1);

    s = samplerObjectIDs[SAMPLER_DEPTH];
    glSamplerParameteri(s, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
    glSamplerParameteri(s, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
}

void buildgl_bindSamplerObject(int texunit, int32_t pth_method)
{
    if (!buildgl_samplerObjectsEnabled())
    {
        gl.currentBoundSampler[texunit] = SAMPLER_NONE;
        glBindSampler(texunit, 0);
        return;
    }

    glsamplertype samplerid = SAMPLER_NEAREST_WRAP;

    switch (pth_method & (PTH_INDEXED|PTH_CLAMPED|PTH_HIGHTILE|PTH_FORCEFILTER|PTH_TEMP_SKY_HACK|PTH_DEPTH_SAMPLER))
    {
        case PTH_DEPTH_SAMPLER:
            samplerid = SAMPLER_DEPTH;
            break;
        case PTH_TEMP_SKY_HACK:
            samplerid = SAMPLER_WRAP_T;
            break;
        case PTH_INDEXED:
            samplerid = videoGetRenderMode() == REND_POLYMER ? SAMPLER_NEAREST_WRAP : SAMPLER_NEAREST_NEAREST_WRAP;
            break;
        case PTH_INDEXED|PTH_CLAMPED:
        case PTH_CLAMPED:
            samplerid = videoGetRenderMode() == REND_POLYMER ? SAMPLER_NEAREST_CLAMP : SAMPLER_NEAREST_NEAREST_CLAMP;
            break;
        case PTH_HIGHTILE:
            samplerid = SAMPLER_WRAP_BOTH;
            break;
        case PTH_HIGHTILE|PTH_CLAMPED:
            samplerid = SAMPLER_CLAMP;
            break;
        case PTH_HIGHTILE|PTH_FORCEFILTER:
            samplerid = SAMPLER_LINEAR_WRAP;
            break;
        case PTH_HIGHTILE|PTH_FORCEFILTER|PTH_CLAMPED:
            samplerid = SAMPLER_LINEAR_CLAMP;
            break;
        case 0:
            samplerid = SAMPLER_NONE;
            break;
    }

    if (gl.currentBoundSampler[texunit] != samplerid)
    {
        gl.currentBoundSampler[texunit] = samplerid;
        glBindSampler(texunit, samplerObjectIDs[samplerid]);
    }
}

static inline void buildgl_multMatrix4f(const float *a, const float *b, float *r)
{
    for (int i=0, j; i<4; i++)
    {
        for (j=0; j<4; j++)
        {
            r[(i<<2)+j] = a[(i<<2)+0] * b[j]
                        + a[(i<<2)+1] * b[4+j]
                        + a[(i<<2)+2] * b[8+j]
                        + a[(i<<2)+3] * b[12+j];
        }
    }
}

static int buildgl_invertMatrix4f(const float *m, float *invOut)
{
    float const inv[16] = { m[5]*m[10]*m[15] - m[5]*m[11]*m[14] - m[9]*m[6]*m[15]
                          + m[9]*m[7]*m[14]  + m[13]*m[6]*m[11] - m[13]*m[7]*m[10],
                           -m[1]*m[10]*m[15] + m[1]*m[11]*m[14] + m[9]*m[2]*m[15]
                          - m[9]*m[3]*m[14]  - m[13]*m[2]*m[11] + m[13]*m[3]*m[10],
                            m[1]*m[6]*m[15] - m[1]*m[7]*m[14] - m[5]*m[2]*m[15]
                          + m[5]*m[3]*m[14] + m[13]*m[2]*m[7] - m[13]*m[3]*m[6],
                           -m[1]*m[6]*m[11] + m[1]*m[7]*m[10] + m[5]*m[2]*m[11]
                          - m[5]*m[3]*m[10] - m[9]*m[2]*m[7]  + m[9]*m[3]*m[6],
                           -m[4]*m[10]*m[15] + m[4]*m[11]*m[14] + m[8]*m[6]*m[15]
                          - m[8]*m[7]*m[14]  - m[12]*m[6]*m[11] + m[12]*m[7]*m[10],
                            m[0]*m[10]*m[15] - m[0]*m[11]*m[14] - m[8]*m[2]*m[15]
                          + m[8]*m[3]*m[14]  + m[12]*m[2]*m[11] - m[12]*m[3]*m[10],
                           -m[0]*m[6]*m[15] + m[0]*m[7]*m[14] + m[4]*m[2]*m[15]
                          - m[4]*m[3]*m[14] - m[12]*m[2]*m[7] + m[12]*m[3]*m[6],
                            m[0]*m[6]*m[11] - m[0]*m[7]*m[10] - m[4]*m[2]*m[11]
                          + m[4]*m[3]*m[10] + m[8]*m[2]*m[7]  - m[8]*m[3]*m[6],
                            m[4]*m[9]*m[15] - m[4]*m[11]*m[13] - m[8]*m[5]*m[15]
                          + m[8]*m[7]*m[13] + m[12]*m[5]*m[11] - m[12]*m[7]*m[9],
                           -m[0]*m[9]*m[15] + m[0]*m[11]*m[13] + m[8]*m[1]*m[15]
                          - m[8]*m[3]*m[13] - m[12]*m[1]*m[11] + m[12]*m[3]*m[9],
                            m[0]*m[5]*m[15] - m[0]*m[7]*m[13] - m[4]*m[1]*m[15]
                          + m[4]*m[3]*m[13] + m[12]*m[1]*m[7] - m[12]*m[3]*m[5],
                           -m[0]*m[5]*m[11] + m[0]*m[7]*m[9] + m[4]*m[1]*m[11]
                          - m[4]*m[3]*m[9]  - m[8]*m[1]*m[7] + m[8]*m[3]*m[5],
                           -m[4]*m[9]*m[14] + m[4]*m[10]*m[13] + m[8]*m[5]*m[14]
                          - m[8]*m[6]*m[13] - m[12]*m[5]*m[10] + m[12]*m[6]*m[9],
                            m[0]*m[9]*m[14] - m[0]*m[10]*m[13] - m[8]*m[1]*m[14]
                          + m[8]*m[2]*m[13] + m[12]*m[1]*m[10] - m[12]*m[2]*m[9],
                           -m[0]*m[5]*m[14] + m[0]*m[6]*m[13] + m[4]*m[1]*m[14]
                          - m[4]*m[2]*m[13] - m[12]*m[1]*m[6] + m[12]*m[2]*m[5],
                            m[0]*m[5]*m[10] - m[0]*m[6]*m[9] - m[4]*m[1]*m[10]
                          + m[4]*m[2]*m[9]  + m[8]*m[1]*m[6] - m[8]*m[2]*m[5]};

    float det = m[0]*inv[0] + m[1]*inv[4] + m[2]*inv[8] + m[3]*inv[12];

    if (!det)
        return 0;

    det = 1.f/det;

    for (int i = 0; i < 16; i++)
        invOut[i] = inv[i] * det;

    return 1;
}

static inline void buildgl_multMatrixVec4f(const float *matrix, const float *in, float *out)
{
    for (int i=0; i<4; i++)
    {
        out[i] = in[0] * matrix[0*4+i]
               + in[1] * matrix[1*4+i]
               + in[2] * matrix[2*4+i]
               + in[3] * matrix[3*4+i];
    }
}

int buildgl_unprojectMatrixToViewport(vec3f_t win, const float *modelMatrix, const float *projMatrix, const int *viewport, float *objx, float *objy, float *objz)
{
    float finalMatrix[16];
    buildgl_multMatrix4f(modelMatrix, projMatrix, finalMatrix);

    if (!buildgl_invertMatrix4f(finalMatrix, finalMatrix))
        return 0;

    float in[4] = { win.x, win.y, win.z, 1.0 };

    /* Map x and y from window coordinates */
    in[0] = (in[0] - viewport[0]) / viewport[2];
    in[1] = (in[1] - viewport[1]) / viewport[3];

    /* Map to range -1 to 1 */
    in[0] = in[0] * 2 - 1;
    in[1] = in[1] * 2 - 1;
    in[2] = in[2] * 2 - 1;

    float out[4];
    buildgl_multMatrixVec4f(finalMatrix, in, out);

    if (!out[3])
        return 0;

    out[0] /= out[3];
    out[1] /= out[3];
    out[2] /= out[3];

    *objx = out[0];
    *objy = out[1];
    *objz = out[2];

    return 1;
}

void buildgl_setPerspective(float fovy, float aspect, float zNear, float zFar)
{
    float const deltaZ = zFar-zNear;
    float const sine   = sinf((fovy *= .5f*fPI*(1.f/180.f)));

    if (!deltaZ || !sine || !aspect)
        return;

    float const cotangent = cosf(fovy)/sine;

    float m[4][4];
    Bmemset(m,0,sizeof(m));

    float const rdeltaZ = 1.f/deltaZ;

    m[0][0] = cotangent/aspect;
    m[1][1] = cotangent;
    m[2][2] = -(zFar+zNear)*rdeltaZ;
    m[2][3] = -1;
    m[3][2] = -2*zNear*zFar*rdeltaZ;

    glMultMatrixf(&m[0][0]);
}

void buildgl_uLookAt(vec3f_t v_eye, vec3f_t v_center, vec3f_t v_up)
{
    float up[3]      = { v_up.x, v_up.y, v_up.z };
    float forward[3] = { v_center.x - v_eye.x,
                         v_center.y - v_eye.y,
                         v_center.z - v_eye.z };

    buildgl_normalize(forward);

    float side[3];
    /* Side = forward x up */
    buildgl_crossproduct(forward, up, side);
    buildgl_normalize(side);

    /* Recompute up as: up = side x forward */
    buildgl_crossproduct(side, forward, up);

    GLfloat m[4][4];
    Bmemset(m, 0, sizeof(m));

    m[0][0] = side[0];
    m[1][0] = side[1];
    m[2][0] = side[2];

    m[0][1] = up[0];
    m[1][1] = up[1];
    m[2][1] = up[2];

    m[0][2] = -forward[0];
    m[1][2] = -forward[1];
    m[2][2] = -forward[2];

    m[3][3] = 1;

    glMultMatrixf(&m[0][0]);
    glTranslatef(-v_eye.x, -v_eye.y, -v_eye.z);
}

#if defined DYNAMIC_GL || defined DYNAMIC_GLEXT
# if !defined RENDERTYPESDL && defined _WIN32
bwglCreateContextProcPtr bwglCreateContext;
bwglDeleteContextProcPtr bwglDeleteContext;
bwglGetProcAddressProcPtr bwglGetProcAddress;
bwglMakeCurrentProcPtr bwglMakeCurrent;

bwglChoosePixelFormatProcPtr bwglChoosePixelFormat;
bwglDescribePixelFormatProcPtr bwglDescribePixelFormat;
bwglGetPixelFormatProcPtr bwglGetPixelFormat;
bwglSetPixelFormatProcPtr bwglSetPixelFormat;

static HMODULE hGLDLL;
char *gldriver = NULL;

static void *getproc_(const char *s, int32_t *err, int32_t fatal, int32_t extension)
{
    void *t;
#if defined RENDERTYPESDL
    UNREFERENCED_PARAMETER(extension);
    t = (void *)SDL_GL_GetProcAddress(s);
#elif defined _WIN32
    if (extension) t = (void *)bwglGetProcAddress(s);
    else t = (void *)GetProcAddress(hGLDLL,s);
#else
#error Need a dynamic loader for this platform...
#endif
    if (!t && fatal)
    {
        initprintf("Failed to find %s in %s\n", s, gldriver);
        *err = 1;
    }
    return t;
}
#define GETPROC(s)        getproc_(s,&err,1,0)

int32_t loadwgl(const char *driver)
{
    int32_t err=0;

    if (hGLDLL) return 0;

    if (!driver)
    {
        driver = "opengl32.dll";
    }

    hGLDLL = LoadLibrary(driver);
    if (!hGLDLL)
    {
        initprintf("Failed loading \"%s\"\n", driver);
        return -1;
    }

    gldriver = Xstrdup(driver);

    bwglCreateContext = (bwglCreateContextProcPtr) GETPROC("wglCreateContext");
    bwglDeleteContext = (bwglDeleteContextProcPtr) GETPROC("wglDeleteContext");
    bwglGetProcAddress = (bwglGetProcAddressProcPtr) GETPROC("wglGetProcAddress");
    bwglMakeCurrent = (bwglMakeCurrentProcPtr) GETPROC("wglMakeCurrent");

    bwglChoosePixelFormat = (bwglChoosePixelFormatProcPtr) GETPROC("wglChoosePixelFormat");
    bwglDescribePixelFormat = (bwglDescribePixelFormatProcPtr) GETPROC("wglDescribePixelFormat");
    bwglGetPixelFormat = (bwglGetPixelFormatProcPtr) GETPROC("wglGetPixelFormat");
    bwglSetPixelFormat = (bwglSetPixelFormatProcPtr) GETPROC("wglSetPixelFormat");

    if (err) unloadwgl();
    return err;
}
int32_t unloadwgl(void)
{
    if (!hGLDLL) return 0;

    DO_FREE_AND_NULL(gldriver);

    FreeLibrary(hGLDLL);
    hGLDLL = NULL;

    bwglCreateContext = (bwglCreateContextProcPtr) NULL;
    bwglDeleteContext = (bwglDeleteContextProcPtr) NULL;
    bwglGetProcAddress = (bwglGetProcAddressProcPtr) NULL;
    bwglMakeCurrent = (bwglMakeCurrentProcPtr) NULL;

    bwglChoosePixelFormat = (bwglChoosePixelFormatProcPtr) NULL;
    bwglDescribePixelFormat = (bwglDescribePixelFormatProcPtr) NULL;
    bwglGetPixelFormat = (bwglGetPixelFormatProcPtr) NULL;
    bwglSetPixelFormat = (bwglSetPixelFormatProcPtr) NULL;
    return 0;
}
# endif
#endif

//////// glGenTextures/glDeleteTextures debugging ////////
# if defined DEBUGGINGAIDS && defined DEBUG_TEXTURE_NAMES
static uint8_t *texnameused;  // bitmap
static uint32_t *texnamefromwhere;  // hash of __FILE__
static uint32_t texnameallocsize;

// djb3 algorithm
static inline uint32_t texdbg_getcode(const char *s)
{
    uint32_t h = 5381;
    int32_t ch;

    while ((ch = *s++) != '\0')
        h = ((h << 5) + h) ^ ch;

    return h;
}

static void texdbg_realloc(uint32_t maxtexname)
{
    uint32_t newsize = texnameallocsize ? texnameallocsize : 64;

    if (maxtexname < texnameallocsize)
        return;

    while (maxtexname >= newsize)
        newsize <<= 1;
//    initprintf("texdebug: new size %u\n", newsize);

    texnameused = Xrealloc(texnameused, newsize>>3);
    texnamefromwhere = Xrealloc(texnamefromwhere, newsize*sizeof(uint32_t));

    Bmemset(texnameused + (texnameallocsize>>3), 0, (newsize-texnameallocsize)>>3);
    Bmemset(texnamefromwhere + texnameallocsize, 0, (newsize-texnameallocsize)*sizeof(uint32_t));

    texnameallocsize = newsize;
}

#undef bglGenTextures
void texdbg_bglGenTextures(GLsizei n, GLuint *textures, const char *srcfn)
{
    int32_t i;
    uint32_t hash = srcfn ? texdbg_getcode(srcfn) : 0;

    for (i=0; i<n; i++)
        if (textures[i] < texnameallocsize && (texnameused[textures[i]>>3]&pow2char[textures[i]&7]))
            initprintf("texdebug %x Gen: overwriting used tex name %u from %x\n", hash, textures[i], texnamefromwhere[textures[i]]);

    bglGenTextures(n, textures);

    {
        GLuint maxtexname = 0;

        for (i=0; i<n; i++)
            maxtexname = max(maxtexname, textures[i]);

        texdbg_realloc(maxtexname);

        for (i=0; i<n; i++)
        {
            texnameused[textures[i]>>3] |= pow2char[textures[i]&7];
            texnamefromwhere[textures[i]] = hash;
        }
    }
}

#undef bglDeleteTextures
void texdbg_bglDeleteTextures(GLsizei n, const GLuint *textures, const char *srcfn)
{
    int32_t i;
    uint32_t hash = srcfn ? texdbg_getcode(srcfn) : 0;

    for (i=0; i<n; i++)
        if (textures[i] < texnameallocsize)
        {
            if ((texnameused[textures[i]>>3]&pow2char[textures[i]&7])==0)
                initprintf("texdebug %x Del: deleting unused tex name %u\n", hash, textures[i]);
            else if ((texnameused[textures[i]>>3]&pow2char[textures[i]&7]) &&
                         texnamefromwhere[textures[i]] != hash)
                initprintf("texdebug %x Del: deleting foreign tex name %u from %x\n", hash,
                           textures[i], texnamefromwhere[textures[i]]);
        }

    bglDeleteTextures(n, textures);

    if (texnameallocsize)
        for (i=0; i<n; i++)
        {
            texnameused[textures[i]>>3] &= ~pow2char[textures[i]&7];
            texnamefromwhere[textures[i]] = 0;
        }
}
# endif  // defined DEBUGGINGAIDS

#endif


code:
/*
 * glsurface.cpp
 *  A 32-bit rendering surface that can quickly blit 8-bit paletted buffers implemented in OpenGL.
 *
 * Copyright � 2018, Alex Dawson. All rights reserved.
 */

#include "glsurface.h"
#include "glad/glad.h"

#include "baselayer.h"
#include "build.h"

static int bufferSize;
static void* buffer;
static GLuint bufferTexID;
static vec2_t bufferRes;

static GLuint paletteTexID;

static GLuint quadVertsID = 0;

static GLuint shaderProgramID = 0;
static GLint texSamplerLoc = -1;
static GLint paletteSamplerLoc = -1;

static GLuint compileShader(GLenum shaderType, const char* const source)
{
    GLuint shaderID = glCreateShader(shaderType);
    if (shaderID == 0)
        return 0;

    const char* const sources[1] = {source};
    glShaderSource(shaderID,
                   1,
                   sources,
                   NULL);
    glCompileShader(shaderID);

    GLint compileStatus;
    glGetShaderiv(shaderID, GL_COMPILE_STATUS, &compileStatus);
    if (!compileStatus)
    {
        GLint logLength;
        glGetShaderiv(shaderID, GL_INFO_LOG_LENGTH, &logLength);
        OSD_Printf("Compile Status: %u\n", compileStatus);
        if (logLength > 0)
        {
            char *infoLog = (char*) Xmalloc(logLength);
            glGetShaderInfoLog(shaderID, logLength, &logLength, infoLog);
            OSD_Printf("Log:\n%s\n", infoLog);
            Xfree(infoLog);
        }
    }

    return shaderID;
}

bool glsurface_initialize(vec2_t bufferResolution)
{
    if (buffer)
        glsurface_destroy();

    buildgl_resetStateAccounting();

    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

    bufferRes  = bufferResolution;
    bufferSize = bufferRes.x * bufferRes.y;

    buffer = Xmalloc(bufferSize);

    if (!glIsBuffer(quadVertsID))
        glGenBuffers(1, &quadVertsID);

    buildgl_bindBuffer(GL_ARRAY_BUFFER, quadVertsID);
    const float quadVerts[] =
        {
            -1.0f,  1.0f, 0.0f, 0.0f, 0.0f, //top-left
            -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, //bottom-left
             1.0f,  1.0f, 0.0f, 1.0f, 0.0f, //top-right
             1.0f, -1.0f, 0.0f, 1.0f, 1.0f  //bottom-right
        };
    glBufferData(GL_ARRAY_BUFFER, sizeof(quadVerts), quadVerts, GL_STATIC_DRAW);

    //specify format/arrangement for vertex positions:
    glVertexAttribPointer(0, 3, GL_FLOAT, false, sizeof(float) * 5, 0);
    //specify format/arrangement for vertex texture coords:
    glVertexAttribPointer(1, 2, GL_FLOAT, false, sizeof(float) * 5, (const void*) (sizeof(float) * 3));

    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);

    buildgl_activeTexture(GL_TEXTURE0);

    if (!glIsTexture(bufferTexID))
        glGenTextures(1, &bufferTexID);

    buildgl_bindTexture(GL_TEXTURE_2D, bufferTexID);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, bufferRes.x, bufferRes.y, 0, GL_RED, GL_UNSIGNED_BYTE, 0);

    glsurface_setPalette(curpalettefaded);

    const char* const VERTEX_SHADER_CODE =
        "#version 110\n\
         \n\
         attribute vec4 i_vertPos;\n\
         attribute vec2 i_texCoord;\n\
         \n\
         varying vec2 v_texCoord;\n\
         \n\
         void main()\n\
         {\n\
             gl_Position = i_vertPos;\n\
             v_texCoord = i_texCoord;\n\
         }\n";
    const char* const FRAGMENT_SHADER_CODE =
        "#version 110\n\
         \n\
         //s_texture points to an indexed color texture\n\
         uniform sampler2D s_texture;\n\
         //s_palette is the palette texture\n\
         uniform sampler2D s_palette;\n\
         \n\
         varying vec2 v_texCoord;\n\
         \n\
         const float c_paletteScale = 255.0/256.0;\n\
         const float c_paletteOffset = 0.5/256.0;\n\
         \n\
         void main()\n\
         {\n\
             vec4 color = texture2D(s_texture, v_texCoord.xy);\n\
             color.r = c_paletteOffset + c_paletteScale*color.r;\n\
             color.rgb = texture2D(s_palette, color.rg).rgb;\n\
             \n\
             // DEBUG \n\
             //color = texture2D(s_palette, v_texCoord.xy);\n\
             //color = texture2D(s_texture, v_texCoord.xy);\n\
             \n\
             gl_FragColor = color;\n\
         }\n";

    if (!glIsProgram(shaderProgramID))
    {
        shaderProgramID = glCreateProgram();

        GLuint vertexShaderID   = compileShader(GL_VERTEX_SHADER, VERTEX_SHADER_CODE);
        GLuint fragmentShaderID = compileShader(GL_FRAGMENT_SHADER, FRAGMENT_SHADER_CODE);
        glAttachShader(shaderProgramID, vertexShaderID);
        glAttachShader(shaderProgramID, fragmentShaderID);
        glBindAttribLocation(shaderProgramID, 0, "i_vertPos");
        glBindAttribLocation(shaderProgramID, 1, "i_texCoord");
        glLinkProgram(shaderProgramID);
        glDetachShader(shaderProgramID, vertexShaderID);
        glDeleteShader(vertexShaderID);
        glDetachShader(shaderProgramID, fragmentShaderID);
        glDeleteShader(fragmentShaderID);
    }
    buildgl_useShaderProgram(shaderProgramID);

    texSamplerLoc = glGetUniformLocation(shaderProgramID, "s_texture");
    paletteSamplerLoc = glGetUniformLocation(shaderProgramID, "s_palette");

    glUniform1i(texSamplerLoc, 0);
    glUniform1i(paletteSamplerLoc, 1);

    return true;
}

void glsurface_destroy()
{
    if (!buffer)
        return;

    DO_FREE_AND_NULL(buffer);

    glDeleteBuffers(1, &quadVertsID);
    quadVertsID = 0;

    glDisableVertexAttribArray(0);
    glDisableVertexAttribArray(1);

    glDeleteTextures(1, &bufferTexID);
    bufferTexID = 0;
    glDeleteTextures(1, &paletteTexID);
    paletteTexID = 0;

    buildgl_useShaderProgram(0);
    glDeleteProgram(shaderProgramID);
    shaderProgramID = 0;
}

void glsurface_setPalette(void* pPalette)
{
    if (!buffer || !pPalette)
        return;

    buildgl_activeTexture(GL_TEXTURE1);

    if (paletteTexID)
    {
        buildgl_bindTexture(GL_TEXTURE_2D, paletteTexID);
        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256, 1, GL_RGBA, GL_UNSIGNED_BYTE, (void*) pPalette);
    }
    else
    {
        glGenTextures(1, &paletteTexID);
        buildgl_bindTexture(GL_TEXTURE_2D, paletteTexID);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, pPalette);
    }
}

void* glsurface_getBuffer()
{
    return buffer;
}

vec2_t glsurface_getBufferResolution()
{
    return bufferRes;
}

void glsurface_blitBuffer()
{
    if (!buffer)
        return;

    buildgl_activeTexture(GL_TEXTURE0);
    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bufferRes.x, bufferRes.y, GL_RED, GL_UNSIGNED_BYTE, (void*) buffer);

    glDrawArrays(GL_TRIANGLE_STRIP,
                 0,
                 4);
}

void glsurface_refresh()
{
    buildgl_activeTexture(GL_TEXTURE0);
    buildgl_bindTexture(GL_TEXTURE_2D, bufferTexID);
}


code:
#include "compat.h"

#include "dynamicgtk.h"

#include "baselayer.h"
#include "build.h"

#include "gtkbits.h"

int32_t gtkenabled = 0;

static GdkPixbuf *appicon = NULL;

static void gtkbuild_runpendingevents(void)
{
    while (gtk_events_pending())
        gtk_main_iteration_do(FALSE);
}

int gtkbuild_msgbox(const char *name, const char *msg)
{
    GtkWidget *dialog;

    if (!gtkenabled) return -1;

    dialog = gtk_message_dialog_new(NULL,
                                    GTK_DIALOG_DESTROY_WITH_PARENT,
                                    GTK_MESSAGE_INFO,
                                    GTK_BUTTONS_OK,
                                    "%s", msg);
    gtk_window_set_title(GTK_WINDOW(dialog), name);
    gtk_dialog_run(GTK_DIALOG(dialog));
    gtk_widget_destroy(dialog);

    gtkbuild_runpendingevents();

    return 1;
}

int gtkbuild_ynbox(const char *name, const char *msg)
{
    int32_t r;
    GtkWidget *dialog;

    if (!gtkenabled) return -1;

    dialog = gtk_message_dialog_new(NULL,
                                    GTK_DIALOG_DESTROY_WITH_PARENT,
                                    GTK_MESSAGE_INFO,
                                    GTK_BUTTONS_YES_NO,
                                    "%s", msg);
    gtk_window_set_title(GTK_WINDOW(dialog), name);
    r = gtk_dialog_run(GTK_DIALOG(dialog));
    gtk_widget_destroy(dialog);

    gtkbuild_runpendingevents();

    if (r == GTK_RESPONSE_YES) return 1;
    return 0;
}

#ifdef RENDERTYPESDL
#include "sdlayer.h"
extern struct sdlappicon sdlappicon;
#endif
void gtkbuild_init(int32_t *argc, char ***argv)
{
    gtkenabled = dynamicgtk_init();
    if (gtkenabled < 0)
    {
        gtkenabled = 0;
        return;
    }

    gtkenabled = gtk_init_check(argc, argv);
    if (!gtkenabled) return;
#ifdef RENDERTYPESDL
    appicon = gdk_pixbuf_new_from_data((const guchar *)sdlappicon.pixels,
                                       GDK_COLORSPACE_RGB, TRUE, 8, sdlappicon.width, sdlappicon.height,
                                       sdlappicon.width*4, NULL, NULL);
#endif
    if (appicon) gtk_window_set_default_icon(appicon);
}

void gtkbuild_exit(int32_t r)
{
    UNREFERENCED_PARAMETER(r);
    if (gtkenabled)
    {
        if (appicon) g_object_unref((gpointer)appicon);
    }

    dynamicgtk_uninit();
}


code:
/*
 * High-colour textures support for Polymost
 * by Jonathon Fowler
 * See the included license file "BUILDLIC.TXT" for license info.
 */

#include "build.h"
#include "hightile.h"
#include "baselayer.h"
#include "compat.h"
#include "engine_priv.h"
#include "kplib.h"

polytint_t hictinting[MAXPALOOKUPS];

hicreplctyp *hicreplc[MAXTILES];
int32_t hicinitcounter = 0;
int32_t usehightile=1;

//
// find the index into hicreplc[] which contains the replacement tile particulars
//
hicreplctyp *hicfindsubst(int picnum, int palnum, int nozero)
{
    if (!hicreplc[picnum] || !hicinitcounter) return nullptr;

    uint8_t const tfn = tilefilenum[picnum];

    do
    {
        for (auto hr = hicreplc[picnum]; hr; hr = hr->next)
            if ((hr->palnum == palnum) & (hr->tfn == tfn))
                return hr;

        if (!palnum | !!nozero)
            return nullptr;

        palnum = 0;
    } while (1);

    return nullptr;	// no replacement found
}

//
// this is separate because it's not worth passing an extra parameter which is "0" in 99.9999% of cases
// to the regular hicfindsubst() function
//
hicreplctyp *hicfindskybox(int picnum, int palnum, int nozero)
{
    if (!hicreplc[picnum] || !hicinitcounter) return nullptr;

    uint8_t const tfn = tilefilenum[picnum];

    do
    {
        for (auto hr = hicreplc[picnum]; hr; hr = hr->next)
            if (hr->skybox && ((hr->palnum == palnum) & (hr->tfn == tfn)))
                return hr;

        if (!palnum | !!nozero)
            return nullptr;

        palnum = 0;
    } while (1);

    return nullptr;	// no replacement found
}

//
// hicinit()
//   Initialize the high-colour stuff to default.
//
void hicinit(void)
{
    int32_t i;

    for (i=0; i<MAXPALOOKUPS; i++)  	// all tints should be 100%
    {
        polytint_t & tint = hictinting[i];
        tint.r = tint.g = tint.b = 0xff;
        tint.f = 0;
    }

    if (hicinitcounter)
    {
        hicreplctyp *hr, *next;
        int32_t j;

        for (i=MAXTILES-1; i>=0; i--)
        {
            for (hr=hicreplc[i]; hr;)
            {
                next = hr->next;

                if (hr->skybox)
                {
                    for (j=5; j>=0; j--)
                        Xfree(hr->skybox->face[j]);
                    Xfree(hr->skybox);
                }

                Xfree(hr->filename);
                Xfree(hr);

                hr = next;
            }
        }
    }

    Bmemset(hicreplc,0,sizeof(hicreplc));

    hicinitcounter++;
}

//
// hicsetpalettetint(pal,r,g,b,sr,sg,sb,effect)
//   The tinting values represent a mechanism for emulating the effect of global sector
//   palette shifts on true-colour textures and only true-colour textures.
//   effect bitset: 1 = greyscale, 2 = invert
//
void hicsetpalettetint(int32_t palnum, char r, char g, char b, char sr, char sg, char sb, polytintflags_t effect)
{
    if ((uint32_t)palnum >= (uint32_t)MAXPALOOKUPS) return;
    if (!hicinitcounter) hicinit();

    polytint_t & tint = hictinting[palnum];
    tint.r = r;
    tint.g = g;
    tint.b = b;
    tint.sr = sr;
    tint.sg = sg;
    tint.sb = sb;
    tint.f = effect;
}

//
// hicsetsubsttex(picnum,pal,filen,alphacut)
//   Specifies a replacement graphic file for an ART tile.
//
int32_t hicsetsubsttex(int32_t picnum, int32_t palnum, const char *filen, float alphacut, float xscale, float yscale, float specpower, float specfactor, char flags)
{
    hicreplctyp *hr, *hrn;

    if ((uint32_t)picnum >= (uint32_t)MAXTILES) return -1;
    if ((uint32_t)palnum >= (uint32_t)MAXPALOOKUPS) return -1;
    if (!hicinitcounter) hicinit();
    uint8_t const tfn = tilefilenum[picnum];
    for (hr = hicreplc[picnum]; hr; hr = hr->next)
    {
        if ((hr->palnum == palnum) & (hr->tfn == tfn))
            break;
    }

    if (!hr)
    {
        // no replacement yet defined
        hrn = (hicreplctyp *)Xcalloc(1,sizeof(hicreplctyp));
        hrn->palnum = palnum;
        hrn->tfn = tfn;
    }
    else hrn = hr;

    // store into hicreplc the details for this replacement
    Xfree(hrn->filename);

    hrn->filename = Xstrdup(filen);
    hrn->alphacut = min(alphacut,1.f);
    hrn->scale.x = xscale;
    hrn->scale.y = yscale;
    hrn->specpower = specpower;
    hrn->specfactor = specfactor;
    hrn->flags = flags;
    if (hr == NULL)
    {
        hrn->next = hicreplc[picnum];
        hicreplc[picnum] = hrn;
    }

    //if (tilesiz[picnum].x<=0 || tilesiz[picnum].y<=0)
    //{
    //    static int32_t first=1;
    //    if (first)
    //    {
    //        LOG_F(WARNING, "Defined replacement for empty tile %d.", picnum);
    //        first = 0;
    //    }
    //}

    //printf("Replacement [%d,%d]: %s\n", picnum, palnum, hicreplc[i]->filename);

    return 0;
}

//
// hicsetskybox(picnum,pal,faces[6])
//   Specifies a graphic files making up a skybox.
//
int32_t hicsetskybox( int32_t picnum, int32_t palnum, char *faces[6], int32_t flags )
{
    hicreplctyp *hr, *hrn;
    int32_t j;

    if ((uint32_t)picnum >= (uint32_t)MAXTILES) return -1;
    if ((uint32_t)palnum >= (uint32_t)MAXPALOOKUPS) return -1;
    for (j=5; j>=0; j--) if (!faces[j]) return -1;
    if (!hicinitcounter) hicinit();
    uint8_t const tfn = tilefilenum[picnum];
    for (hr = hicreplc[picnum]; hr; hr = hr->next)
    {
        if ((hr->palnum == palnum) & (hr->tfn == tfn))
            break;
    }

    if (!hr)
    {
        // no replacement yet defined
        hrn = (hicreplctyp *)Xcalloc(1,sizeof(hicreplctyp));
        hrn->palnum = palnum;
        hrn->tfn = tfn;
    }
    else hrn = hr;

    if (!hrn->skybox)
        hrn->skybox = (struct hicskybox_t *)Xcalloc(1,sizeof(struct hicskybox_t));
    else
    {
        for (j=0; j<6; j++)
            DO_FREE_AND_NULL(hrn->skybox->face[j]);
    }

    // store each face's filename
    for (j=0; j<6; j++)
        hrn->skybox->face[j] = Xstrdup(faces[j]);

    hrn->flags = flags;

    if (hr == NULL)
    {
        hrn->next = hicreplc[picnum];
        hicreplc[picnum] = hrn;
    }

    return 0;
}

//
// hicclearsubst(picnum,pal)
//   Clears a replacement for an ART tile, including skybox faces.
//
int32_t hicclearsubst(int32_t picnum, int32_t palnum)
{
    hicreplctyp *hr, *hrn = NULL;

    if ((uint32_t)picnum >= (uint32_t)MAXTILES) return -1;
    if ((uint32_t)palnum >= (uint32_t)MAXPALOOKUPS) return -1;
    if (!hicinitcounter) return 0;
    uint8_t const tfn = tilefilenum[picnum];
    for (hr = hicreplc[picnum]; hr; hrn = hr, hr = hr->next)
    {
        if ((hr->palnum == palnum) & (hr->tfn == tfn))
            break;
    }

    if (!hr) return 0;

    Xfree(hr->filename);
    if (hr->skybox)
    {
        int32_t i;
        for (i=5; i>=0; i--)
            Xfree(hr->skybox->face[i]);
        Xfree(hr->skybox);
    }

    if (hrn) hrn->next = hr->next;
    else hicreplc[picnum] = hr->next;
    Xfree(hr);

    return 0;
}

void hictinting_applypixcolor(coltype* tcol, uint8_t pal, bool no_rb_swap)
{
    polytintflags_t const effect = hicfxmask(pal);
    polytint_t const& tint = hictinting[pal];

    int32_t r = (!no_rb_swap && glinfo.bgra) ? tint.b : tint.r;
    int32_t g = tint.g;
    int32_t b = (!no_rb_swap && glinfo.bgra) ? tint.r : tint.b;

    if (effect & HICTINT_GRAYSCALE)
    {
        tcol->g = tcol->r = tcol->b = (uint8_t)((tcol->b * GRAYSCALE_COEFF_RED) +
            (tcol->g * GRAYSCALE_COEFF_GREEN) +
            (tcol->r * GRAYSCALE_COEFF_BLUE));
    }

    if (effect & HICTINT_INVERT)
    {
        tcol->b = 255 - tcol->b;
        tcol->g = 255 - tcol->g;
        tcol->r = 255 - tcol->r;
    }

    if (effect & HICTINT_COLORIZE)
    {
        tcol->b = min((int32_t)((tcol->b) * b) >> 6, 255);
        tcol->g = min((int32_t)((tcol->g) * g) >> 6, 255);
        tcol->r = min((int32_t)((tcol->r) * r) >> 6, 255);
    }

    switch (effect & HICTINT_BLENDMASK)
    {
    case HICTINT_BLEND_SCREEN:
        tcol->b = 255 - (((255 - tcol->b) * (255 - b)) >> 8);
        tcol->g = 255 - (((255 - tcol->g) * (255 - g)) >> 8);
        tcol->r = 255 - (((255 - tcol->r) * (255 - r)) >> 8);
        break;
    case HICTINT_BLEND_OVERLAY:
        tcol->b = tcol->b < 128 ? (tcol->b * b) >> 7 : 255 - (((255 - tcol->b) * (255 - b)) >> 7);
        tcol->g = tcol->g < 128 ? (tcol->g * g) >> 7 : 255 - (((255 - tcol->g) * (255 - g)) >> 7);
        tcol->r = tcol->r < 128 ? (tcol->r * r) >> 7 : 255 - (((255 - tcol->r) * (255 - r)) >> 7);
        break;
    case HICTINT_BLEND_HARDLIGHT:
        tcol->b = b < 128 ? (tcol->b * b) >> 7 : 255 - (((255 - tcol->b) * (255 - b)) >> 7);
        tcol->g = g < 128 ? (tcol->g * g) >> 7 : 255 - (((255 - tcol->g) * (255 - g)) >> 7);
        tcol->r = r < 128 ? (tcol->r * r) >> 7 : 255 - (((255 - tcol->r) * (255 - r)) >> 7);
        break;
    }
}