code:
//-------------------------------------------------------------------------
/*
Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 sirlemonhead, Nuke.YKT

This file is part of PCExhumed.

PCExhumed is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/
//-------------------------------------------------------------------------

#include "compat.h"
#include "keyboard.h"
#include "control.h"
#include "engine.h"
#include "config.h"
#include "names.h"
#include "view.h"
#include "status.h"
#include "exhumed.h"
#include "ramses.h"
#include "player.h"
#include "snake.h"
#include "gun.h"
#include "light.h"
#include "init.h"
#include "menu.h"
#include "keyboard.h"
#include "cd.h"
#include "typedefs.h"
#include "map.h"
#include "move.h"
#include "sound.h"
#include "engine.h"
#include "save.h"
#include "trigdat.h"
#include "runlist.h"
#include <string.h>

short bSubTitles = kTrue;

int zbob;

fix16_t nDestVertPan[kMaxPlayers] = { 0 };
short dVertPan[kMaxPlayers];
fix16_t nVertPan[kMaxPlayers];
int nCamerax;
int nCameray;
int nCameraz;

short bTouchFloor;

short nQuake[kMaxPlayers] = { 0 };

short nChunkTotal = 0;

short nViewTop;
short nViewBottom;
short nViewRight;
short nViewLeft;
short bClip = kFalse;
short bCamera = kFalse;

int gFov;
short nViewy;

short besttarget;
short enemy;
short nEnemyPal = 0;

#define MAXINTERPOLATIONS MAXSPRITES
int32_t g_interpolationCnt;
int32_t oldipos[MAXINTERPOLATIONS];
int32_t* curipos[MAXINTERPOLATIONS];
int32_t bakipos[MAXINTERPOLATIONS];

int32_t nCameraDist = 0;
int32_t nCameraClock = 0;

int viewSetInterpolation(int32_t *const posptr)
{
    if (g_interpolationCnt >= MAXINTERPOLATIONS)
        return 1;

    for (bssize_t i = 0; i < g_interpolationCnt; ++i)
        if (curipos[i] == posptr)
            return 0;

    curipos[g_interpolationCnt] = posptr;
    oldipos[g_interpolationCnt] = *posptr;
    g_interpolationCnt++;
    return 0;
}

void viewStopInterpolation(const int32_t * const posptr)
{
    for (bssize_t i = 0; i < g_interpolationCnt; ++i)
        if (curipos[i] == posptr)
        {
            g_interpolationCnt--;
            oldipos[i] = oldipos[g_interpolationCnt];
            bakipos[i] = bakipos[g_interpolationCnt];
            curipos[i] = curipos[g_interpolationCnt];
        }
}

void viewDoInterpolations(int smoothRatio)
{
    int32_t ndelta = 0;

    for (bssize_t i = 0, j = 0; i < g_interpolationCnt; ++i)
    {
        int32_t const odelta = ndelta;
        bakipos[i] = *curipos[i];
        ndelta = (*curipos[i]) - oldipos[i];
        if (odelta != ndelta)
            j = mulscale16(ndelta, smoothRatio);
        *curipos[i] = oldipos[i] + j;
    }
}

void viewUpdateInterpolations(void)  //Stick at beginning of G_DoMoveThings
{
    for (bssize_t i=g_interpolationCnt-1; i>=0; i--) oldipos[i] = *curipos[i];
}

void viewRestoreInterpolations(void)  //Stick at end of drawscreen
{
    int32_t i=g_interpolationCnt-1;

    for (; i>=0; i--) *curipos[i] = bakipos[i];
}

void InitView()
{
#ifdef USE_OPENGL
    polymostcenterhoriz = 92;
#endif
}

// NOTE - not to be confused with Ken's analyzesprites()
static void analyzesprites()
{
    short nPlayerSprite = PlayerList[nLocalPlayer].nSprite;

    int var_38 = 20;
    int var_2C = 30000;

    spritetype *pPlayerSprite = &sprite[nPlayerSprite];

    besttarget = -1;

    int x = pPlayerSprite->x;
    int y = pPlayerSprite->y;

    int z = pPlayerSprite->z - (GetSpriteHeight(nPlayerSprite) / 2);

    short nSector = pPlayerSprite->sectnum;

    int nAngle = (2048 - pPlayerSprite->ang) & kAngleMask;

    int nTSprite;
    tspritetype *pTSprite;

//	int var_20 = var_24;

    for (nTSprite = spritesortcnt-1, pTSprite = &tsprite[nTSprite]; nTSprite >= 0; nTSprite--, pTSprite--)
    {
        int nSprite = pTSprite->owner;
        spritetype *pSprite = &sprite[nSprite];

        if (pTSprite->sectnum >= 0)
        {
            sectortype *pSector = &sector[pTSprite->sectnum];
            int nSectShade = (pSector->ceilingstat & 1) ? pSector->ceilingshade : pSector->floorshade;
            int nShade = pTSprite->shade + nSectShade + 6;
            pTSprite->shade = clamp(nShade, -128, 127);
        }

        pTSprite->pal = RemapPLU(pTSprite->pal);

        // PowerSlaveGDX: Torch bouncing fix
        if ((pTSprite->picnum == kTile338 || pTSprite->picnum == kTile350) && (pTSprite->cstat & 0x80) == 0)
        {
            pTSprite->cstat |= 0x80;
            int nTileY = (tilesiz[pTSprite->picnum].y * pTSprite->yrepeat) * 2;
            pTSprite->z -= nTileY;
        }

        if (pSprite->statnum > 0)
        {
            runlist_SignalRun(pSprite->lotag - 1, nTSprite | 0x90000);

            if ((pSprite->statnum < 150) && (pSprite->cstat & 0x101) && (nSprite != nPlayerSprite))
            {
                int xval = pSprite->x - x;
                int yval = pSprite->y - y;

                int vcos = Cos(nAngle);
                int vsin = Sin(nAngle);

                int edx = ((vcos * yval) + (xval * vsin)) >> 14;
                int ebx = klabs(((vcos * xval) - (yval * vsin)) >> 14);

                if (!ebx)
                    continue;

                edx = (klabs(edx) * 32) / ebx;
                if (ebx < 1000 && ebx < var_2C && edx < 10)
                {
                    besttarget = nSprite;
                    var_38 = edx;
                    var_2C = ebx;
                }
                else if (ebx < 30000)
                {
                    int t = var_38 - edx;
                    if (t > 3 || (ebx < var_2C && klabs(t) < 5))
                    {
                        var_38 = edx;
                        var_2C = ebx;
                        besttarget = nSprite;
                    }
                }
            }
        }

        if (tilehasmodelorvoxel(pTSprite->picnum, pTSprite->pal) && !(spriteext[nSprite].flags & SPREXT_NOTMD))
        {
            int const nRootTile = pTSprite->picnum;
            int const nVoxel = tiletovox[pTSprite->picnum];

            if (nVoxel != -1 && ((voxrotate[nVoxel >> 3] & pow2char[nVoxel & 7]) != 0 || (picanm[nRootTile].extra & 7) == 7))
                pTSprite->ang = (pTSprite->ang + ((int)totalclock << 3)) & kAngleMask;

            if (pTSprite->picnum == 736) // invincibility sprite needs to always face player
                pTSprite->ang = getangle(nCamerax - pTSprite->x, nCameray - pTSprite->y); // TODO - CHECKME: have to set pSprite not PTSprite here or angle won't change??
        }
    }
    if (besttarget != -1)
    {
        spritetype *pTarget = &sprite[besttarget];

        nCreepyTimer = kCreepyCount;

        if (!cansee(x, y, z, nSector, pTarget->x, pTarget->y, pTarget->z - GetSpriteHeight(besttarget), pTarget->sectnum))
        {
            besttarget = -1;
        }
    }
}

void ResetView()
{
    videoSetGameMode(gSetup.fullscreen, gSetup.xdim, gSetup.ydim, gSetup.bpp, 0);
    DoOverscanSet(overscanindex);
    EraseScreen(overscanindex);
    memcpy(curpalettefaded, curpalette, sizeof(curpalette));
    videoUpdatePalette(0, 256);
#ifdef USE_OPENGL
    videoTintBlood(0, 0, 0);
#endif

    LoadStatus();
}

void SetView1()
{
}

void FlushMessageLine()
{
    int tileX = tilesiz[nBackgroundPic].x;
    int nTileOffset = 0;

    int xPos = 0;

    while (xPos < xdim)
    {
        overwritesprite(xPos, 0, nBackgroundPic + nTileOffset, -32, 0, kPalNormal);

        nTileOffset = nTileOffset == 0;

        xPos += tileX;
    }
}

void RefreshBackground()
{
    if (screensize <= 0)
        return;
    int nTileOffset = 0;
    int tileX = tilesiz[nBackgroundPic].x;
    int tileY = tilesiz[nBackgroundPic].y;

    videoSetViewableArea(0, 0, xdim - 1, ydim - 1);

    MaskStatus();

    for (int y = 0; y < nViewTop; y += tileY)
    {
        nTileOffset = (y/tileY)&1;
        for (int x = 0; x < xdim; x += tileX)
        {
            rotatesprite(x<<16, y<<16, 65536L, 0, nBackgroundPic + nTileOffset, -32, kPalNormal, 8 + 16 + 64, 0, 0, xdim-1, nViewTop-1);
            nTileOffset ^= 1;
        }
    }
    for (int y = (nViewTop/tileY)*tileY; y <= nViewBottom; y += tileY)
    {
        nTileOffset = (y/tileY)&1;
        for (int x = 0; x < nViewLeft; x += tileX)
        {
            rotatesprite(x<<16, y<<16, 65536L, 0, nBackgroundPic + nTileOffset, -32, kPalNormal, 8 + 16 + 64, 0, nViewTop, nViewLeft-1, nViewBottom);
            nTileOffset ^= 1;
        }
    }
    for (int y = (nViewTop/tileY)*tileY; y <= nViewBottom; y += tileY)
    {
        nTileOffset = ((y/tileY)^((nViewRight+1)/tileX))&1;
        for (int x = ((nViewRight+1)/tileX)*tileX; x < xdim; x += tileX)
        {
            rotatesprite(x<<16, y<<16, 65536L, 0, nBackgroundPic + nTileOffset, -32, kPalNormal, 8 + 16 + 64, nViewRight+1, nViewTop, xdim-1, nViewBottom);
            nTileOffset ^= 1;
        }
    }
    for (int y = ((nViewBottom+1)/tileY)*tileY; y < ydim; y += tileY)
    {
        nTileOffset = (y/tileY)&1;
        for (int x = 0; x < xdim; x += tileX)
        {
            rotatesprite(x<<16, y<<16, 65536L, 0, nBackgroundPic + nTileOffset, -32, kPalNormal, 8 + 16 + 64, 0, nViewBottom+1, xdim-1, ydim-1);
            nTileOffset ^= 1;
        }
    }

    videoSetViewableArea(nViewLeft, nViewTop, nViewRight, nViewBottom);
}

void MySetView(int x1, int y1, int x2, int y2)
{
    if (!bFullScreen) {
        MaskStatus();
    }

    nViewLeft = x1;
    nViewTop = y1;
    nViewRight = x2;
    nViewBottom = y2;

    videoSetViewableArea(x1, y1, x2, y2);

    nViewy = y1;
}

// unused function
void TestLava()
{
}

static inline int interpolate16(int a, int b, int smooth)
{
    return a + mulscale16(b - a, smooth);
}

static inline fix16_t q16angle_interpolate16(fix16_t a, fix16_t b, int smooth)
{
    return a + mulscale16(((b+F16(1024)-a)&0x7FFFFFF)-F16(1024), smooth);
}

void viewDo3rdPerson(int nSprite, vec3_t* v, short* nSector, short nAngle, int horiz)
{
    spritetype* sp;
    int i, nx, ny, nz, hx, hy /*, hz*/;
    short bakcstat, daang;
    hitdata_t hitinfo;

    nx = Cos(nAngle + 1024) >> 4;
    ny = Sin(nAngle + 1024) >> 4;
    nz = (horiz - 100) * 128;

    sp = &sprite[nSprite];

    bakcstat = sp->cstat;
    sp->cstat &= (short)~0x101;

    updatesectorz(v->x, v->y, v->z, nSector);
    hitscan(v, *nSector, nx, ny, nz, &hitinfo, CLIPMASK1);

    hx = hitinfo.x - v->x;
    hy = hitinfo.y - v->y;

    if (klabs(nx) + klabs(ny) > klabs(hx) + klabs(hy))
    {
        *nSector = hitinfo.sect;
        if (hitinfo.wall >= 0)
        {
            daang = getangle(wall[wall[hitinfo.wall].point2].x - wall[hitinfo.wall].x,
                wall[wall[hitinfo.wall].point2].y - wall[hitinfo.wall].y);

            i = nx * sintable[daang] + ny * sintable[(daang + 1536) & 2047];
            if (klabs(nx) > klabs(ny)) {
                hx -= mulscale28(nx, i);
            }
            else {
                hy -= mulscale28(ny, i);
            }
        }
        else if (hitinfo.sprite < 0)
        {
            if (klabs(nx) > klabs(ny)) {
                hx -= (nx >> 5);
            }
            else {
                hy -= (ny >> 5);
            }
        }
        if (klabs(nx) > klabs(ny)) {
            i = divscale16(hx, nx);
        }
        else {
            i = divscale16(hy, ny);
        }
        if (i < nCameraDist) {
            nCameraDist = i;
        }
    }

    v->x = v->x + mulscale16(nx, nCameraDist);
    v->y = v->y + mulscale16(ny, nCameraDist);
    v->z = v->z + mulscale16(nz, nCameraDist);

    updatesectorz(v->x, v->y, v->z, nSector);

    sp->cstat = bakcstat;
}

void DrawView(int smoothRatio)
{
    int playerX;
    int playerY;
    int playerZ;
    short nSector;
    fix16_t nAngle;
    fix16_t pan;

    fix16_t nCameraa;
    fix16_t nCamerapan;

    int viewz;

#if 0
    if (bgpages <= 0)
    {
        if (textpages > 0)
        {
            textpages--;
            FlushMessageLine();
        }
    }
    else
    {
        RefreshBackground();
        bgpages--;
    }
#else
    FlushMessageLine();
    RefreshBackground();
#endif

    if (!bFullScreen) {
        MaskStatus();
    }

    zbob = Sin(2 * bobangle) >> 3;

    int nPlayerSprite = PlayerList[nLocalPlayer].nSprite;
    int nPlayerOldCstat = sprite[nPlayerSprite].cstat;
    int nDoppleOldCstat = sprite[nDoppleSprite[nLocalPlayer]].cstat;

    if (nSnakeCam >= 0)
    {
        int nSprite = SnakeList[nSnakeCam].nSprites[0];

        playerX = sprite[nSprite].x;
        playerY = sprite[nSprite].y;
        playerZ = sprite[nSprite].z;
        nSector = sprite[nSprite].sectnum;
        nAngle = fix16_from_int(sprite[nSprite].ang);

        SetGreenPal();
        UnMaskStatus();

        enemy = SnakeList[nSnakeCam].nEnemy;

        if (enemy <= -1 || totalmoves & 1)
        {
            nEnemyPal = -1;
        }
        else
        {
            nEnemyPal = sprite[enemy].pal;
            sprite[enemy].pal = 5;
        }
    }
    else
    {
        playerX = interpolate16(PlayerList[nLocalPlayer].opos.x, sprite[nPlayerSprite].x, smoothRatio);
        playerY = interpolate16(PlayerList[nLocalPlayer].opos.y, sprite[nPlayerSprite].y, smoothRatio);
        playerZ = interpolate16(PlayerList[nLocalPlayer].opos.z, sprite[nPlayerSprite].z, smoothRatio)
                + interpolate16(oeyelevel[nLocalPlayer], eyelevel[nLocalPlayer], smoothRatio);
        nSector = nPlayerViewSect[nLocalPlayer];
        nAngle = q16angle_interpolate16(PlayerList[nLocalPlayer].q16oangle, PlayerList[nLocalPlayer].q16angle, smoothRatio);

        if (!bCamera)
        {
            sprite[nPlayerSprite].cstat |= CSTAT_SPRITE_INVISIBLE;
            sprite[nDoppleSprite[nLocalPlayer]].cstat |= CSTAT_SPRITE_INVISIBLE;
        }
        else
        {
            sprite[nPlayerSprite].cstat |= CSTAT_SPRITE_TRANSLUCENT;
            sprite[nDoppleSprite[nLocalPlayer]].cstat |= CSTAT_SPRITE_INVISIBLE;
        }
    }

    nCameraa = nAngle;

    if (!bCamera || nFreeze)
    {
        if (nSnakeCam >= 0)
        {
            pan = F16(92);
            viewz = playerZ;
        }
        else
        {
            viewz = playerZ + nQuake[nLocalPlayer];
            int floorZ = sector[sprite[nPlayerSprite].sectnum].floorz;

            // pan = nVertPan[nLocalPlayer];
            pan = interpolate16(PlayerList[nLocalPlayer].q16ohoriz, PlayerList[nLocalPlayer].q16horiz, smoothRatio);

            if (viewz > floorZ)
                viewz = floorZ;

            nCameraa += fix16_from_int((nQuake[nLocalPlayer] >> 7) % 31);
            nCameraa &= 0x7FFFFFF;
        }
    }
    else
    {
        // code from above
        viewz = playerZ + nQuake[nLocalPlayer];
        int floorZ = sector[sprite[nPlayerSprite].sectnum].floorz;

        pan = interpolate16(PlayerList[nLocalPlayer].q16ohoriz, PlayerList[nLocalPlayer].q16horiz, smoothRatio);

        if (viewz > floorZ)
            viewz = floorZ;

        nCameraa += fix16_from_int((nQuake[nLocalPlayer] >> 7) % 31);
        nCameraa &= 0x7FFFFFF;

        vec3_t cpos;
        cpos.x = playerX;
        cpos.y = playerY;
        cpos.z = playerZ;

        viewDo3rdPerson(PlayerList[nLocalPlayer].nSprite, &cpos, &nSector, inita, fix16_to_int(pan));

        playerX = cpos.x;
        playerY = cpos.y;
        playerZ = cpos.z;
    }

    nCamerax = playerX;
    nCameray = playerY;
    nCameraz = playerZ;

    int Z = sector[nSector].ceilingz + 256;
    if (Z <= viewz)
    {
        Z = sector[nSector].floorz - 256;

        if (Z < viewz)
            viewz = Z;
    }
    else {
        viewz = Z;
    }

    nCamerapan = pan;

    if (nFreeze == 2 || nFreeze == 1)
    {
        nSnakeCam = -1;
        videoSetViewableArea(0, 0, xdim - 1, ydim - 1);
        UnMaskStatus();
    }

    UpdateMap();

    if (nFreeze != 3)
    {
        static uint8_t sectorFloorPal[MAXSECTORS];
        static uint8_t sectorCeilingPal[MAXSECTORS];
        static uint8_t wallPal[MAXWALLS];
        int const viewingRange = viewingrange;
        int const vr = Blrintf(65536.f * tanf(gFov * (fPI / 360.f)));

        if (r_usenewaspect)
        {
            newaspect_enable = 1;
            videoSetCorrectedAspect();
            renderSetAspect(mulscale16(vr, viewingrange), yxaspect);
        }
        else
            renderSetAspect(vr, yxaspect);

        if (HavePLURemap())
        {
            for (int i = 0; i < numsectors; i++)
            {
                sectorFloorPal[i] = sector[i].floorpal;
                sectorCeilingPal[i] = sector[i].ceilingpal;
                sector[i].floorpal = RemapPLU(sectorFloorPal[i]);
                sector[i].ceilingpal = RemapPLU(sectorCeilingPal[i]);
            }
            for (int i = 0; i < numwalls; i++)
            {
                wallPal[i] = wall[i].pal;
                wall[i].pal = RemapPLU(wallPal[i]);
            }
        }

        renderDrawRoomsQ16(nCamerax, nCameray, viewz, nCameraa, nCamerapan, nSector);
        analyzesprites();
        renderDrawMasks();

        if (HavePLURemap())
        {
            for (int i = 0; i < numsectors; i++)
            {
                sector[i].floorpal = sectorFloorPal[i];
                sector[i].ceilingpal = sectorCeilingPal[i];
            }
            for (int i = 0; i < numwalls; i++)
            {
                wall[i].pal = wallPal[i];
            }
        }

        if (r_usenewaspect)
        {
            newaspect_enable = 0;
            renderSetAspect(viewingRange, tabledivide32_noinline(65536 * ydim * 8, xdim * 5));
        }

        if (nFreeze)
        {
            nSnakeCam = -1;

            if (nFreeze == 2)
            {
                if (nHeadStage == 4)
                {
                    nHeadStage = 5;

                    sprite[nPlayerSprite].cstat |= 0x8000;

                    int ang2 = fix16_to_int(nCameraa) - sprite[nPlayerSprite].ang;
                    if (ang2 < 0)
                        ang2 = -ang2;

                    if (ang2 > 10)
                    {
                        inita -= (ang2 >> 3);
                        return;
                    }

                    if (bSubTitles)
                    {
                        if (levelnum == 1)
                            ReadyCinemaText(1);
                        else
                            ReadyCinemaText(5);
                    }
                }
                else
                {
                    if ((bSubTitles && !AdvanceCinemaText()) || KB_KeyDown[sc_Escape] || KB_KeyDown[sc_Return] || KB_KeyDown[sc_Space])
                    {
                        levelnew = levelnum + 1;

                        if (CDplaying()) {
                            fadecdaudio();
                        }
                    }

                    videoSetViewableArea(nViewLeft, nViewTop, nViewRight, nViewBottom);
                }
            }
        }
        else
        {
            if (nSnakeCam < 0)
            {
                DrawWeapons(smoothRatio);
                if (gShowCrosshair) {
                    if (!waloff[kTile1579]) tileLoad(kTile1579);
                    rotatesprite(160<<16, 92<<16, 65536, 0, kTile1579, 0, 0, 1+2, windowxy1.x, windowxy1.y, windowxy2.x, windowxy2.y);
                }
                DrawMap();
                DrawStatus();
            }
            else
            {
                RestoreGreenPal();
                if (nEnemyPal > -1) {
                    sprite[enemy].pal = nEnemyPal;
                }

                DrawMap();

                if (!bFullScreen) {
                    MaskStatus();
                }

                DrawSnakeCamStatus();
            }
        }
    }
    else
    {
        videoClearScreen(overscanindex);
        DrawStatus();
    }

    sprite[nPlayerSprite].cstat = nPlayerOldCstat;
    sprite[nDoppleSprite[nLocalPlayer]].cstat = nDoppleOldCstat;

    flash = 0;
}

void NoClip()
{
    videoSetViewableArea(0, 0, xdim - 1, ydim - 1);

    bClip = kFalse;
}

void Clip()
{
    videoSetViewableArea(nViewLeft, nViewTop, nViewRight, nViewBottom);
    if (!bFullScreen) {
        MaskStatus();
    }

    bClip = kTrue;
}

class ViewLoadSave : public LoadSave
{
public:
    virtual void Load();
    virtual void Save();
};

void ViewLoadSave::Load()
{
    Read(nDestVertPan, sizeof(nDestVertPan));
    Read(dVertPan, sizeof(dVertPan));
    Read(nVertPan, sizeof(nVertPan));
    Read(&nCamerax, sizeof(nCamerax));
    Read(&nCameray, sizeof(nCameray));
    Read(&nCameraz, sizeof(nCameraz));
    Read(&bTouchFloor, sizeof(bTouchFloor));
    Read(nQuake, sizeof(nQuake));
    Read(&nChunkTotal, sizeof(nChunkTotal));
    Read(&besttarget, sizeof(besttarget));

    // TODO - view vars?

    Read(&bCamera, sizeof(bCamera));
    Read(&enemy, sizeof(enemy));
    Read(&nEnemyPal, sizeof(nEnemyPal));
}

void ViewLoadSave::Save()
{
    Write(nDestVertPan, sizeof(nDestVertPan));
    Write(dVertPan, sizeof(dVertPan));
    Write(nVertPan, sizeof(nVertPan));
    Write(&nCamerax, sizeof(nCamerax));
    Write(&nCameray, sizeof(nCameray));
    Write(&nCameraz, sizeof(nCameraz));
    Write(&bTouchFloor, sizeof(bTouchFloor));
    Write(nQuake, sizeof(nQuake));
    Write(&nChunkTotal, sizeof(nChunkTotal));
    Write(&besttarget, sizeof(besttarget));

    // TODO - view vars?

    Write(&bCamera, sizeof(bCamera));
    Write(&enemy, sizeof(enemy));
    Write(&nEnemyPal, sizeof(nEnemyPal));
}

static ViewLoadSave* myLoadSave;

void ViewLoadSaveConstruct()
{
    myLoadSave = new ViewLoadSave();
}


code:
//-------------------------------------------------------------------------
/*
Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 sirlemonhead, Nuke.YKT

This file is part of PCExhumed.

PCExhumed is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/
//-------------------------------------------------------------------------

#ifndef __view_h__
#define __view_h__

#include "build.h"

extern short bSubTitles;
extern short nViewTop;
extern short bClip;
extern short nViewBottom;
extern short nViewRight;
extern short nViewLeft;
extern short besttarget;
extern short bCamera;

void InitView();
void SetView1();
void RefreshBackground();
void DrawView(int smoothRatio);
void MySetView(int x1, int y1, int x2, int y2);
void ResetView();
void NoClip();
void Clip();

int viewSetInterpolation(int32_t *const posptr);
void viewStopInterpolation(const int32_t * const posptr);
void viewDoInterpolations(int smoothRatio);
void viewUpdateInterpolations(void);
void viewRestoreInterpolations(void);

extern fix16_t nDestVertPan[];
extern short dVertPan[];
extern fix16_t nVertPan[];
extern short nQuake[];
extern int nCamerax;
extern int nCameray;
extern int nCameraz;
extern short bTouchFloor;
extern short nChunkTotal;
extern int gFov;
extern int32_t nCameraDist;
extern int32_t nCameraClock;

static inline int angle_interpolate16(int a, int b, int smooth)
{
    return a + mulscale16(((b+1024-a)&2047)-1024, smooth);
}

#endif

repo_name: orochi/nblood

license: mit


code:
//-------------------------------------------------------------------------
/*
Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 sirlemonhead, Nuke.YKT

This file is part of PCExhumed.

PCExhumed is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/
//-------------------------------------------------------------------------

#include "wasp.h"
#include "engine.h"
#include "runlist.h"
#include "random.h"
#include "exhumed.h"
#include "sequence.h"
#include "init.h"
#include "move.h"
#include "anims.h"
#include "trigdat.h"
#include <assert.h>
#include "save.h"

#define kMaxWasps   100

static short nVelShift = 0;
short nWaspCount;

struct Wasp
{
    short nHealth;
    short nFrame;
    short nAction;
    short nSprite;
    short nRun;
    short nTarget;
    short field_C;
    short nAttackTime;
    short dTime;
    short nVelocity;
    short field_14;
    short nDamage;
};

Wasp WaspList[kMaxWasps];

static actionSeq ActionSeq[] = {
    {0,  0},
    {0,  0},
    {9,  0},
    {18, 0},
    {27, 1},
    {28, 1},
    {29, 1}
};

void InitWasps()
{
    nWaspCount = 0;
}

void SetWaspVel(short nSprite)
{
    if (nVelShift < 0)
    {
        sprite[nSprite].xvel = Cos(sprite[nSprite].ang) << -nVelShift;
        sprite[nSprite].yvel = Sin(sprite[nSprite].ang) << -nVelShift;
    }
    else
    {
        sprite[nSprite].xvel = Cos(sprite[nSprite].ang) >> nVelShift;
        sprite[nSprite].yvel = Sin(sprite[nSprite].ang) >> nVelShift;
    }
}

int GetWaspSprite(int nWasp)
{
    assert(nWasp >= 0 && nWasp < kMaxWasps);
    return WaspList[nWasp].nSprite;
}

int BuildWasp(short nSprite, int x, int y, int z, short nSector, short nAngle)
{
    if (nWaspCount >= kMaxWasps) {
        return -1;
    }

    short nWasp = nWaspCount;
    nWaspCount++;

    uint8_t bEggWasp = kFalse;
    if (nSprite == -2) {
        bEggWasp = kTrue;
    }

    if (nSprite < 0)
    {
        nSprite = insertsprite(nSector, 107);
        assert(nSprite >= 0 && nSprite < kMaxSprites);

        sprite[nSprite].x = x;
        sprite[nSprite].y = y;
        sprite[nSprite].z = z;
    }
    else
    {
        nAngle = sprite[nSprite].ang;
        changespritestat(nSprite, 107);
    }

    sprite[nSprite].shade = -12;
    sprite[nSprite].cstat = 0x101;
    sprite[nSprite].pal = sector[sprite[nSprite].sectnum].ceilingpal;
    sprite[nSprite].clipdist = 70;

    if (bEggWasp)
    {
        sprite[nSprite].xrepeat = 20;
        sprite[nSprite].yrepeat = 20;
    }
    else
    {
        sprite[nSprite].xrepeat = 50;
        sprite[nSprite].yrepeat = 50;
    }

    sprite[nSprite].xoffset = 0;
    sprite[nSprite].yoffset = 0;
    sprite[nSprite].picnum = 1;
    sprite[nSprite].ang = nAngle;
    sprite[nSprite].xvel = 0;
    sprite[nSprite].yvel = 0;
    sprite[nSprite].zvel = 0;
    sprite[nSprite].hitag = 0;
    sprite[nSprite].lotag = runlist_HeadRun() + 1;
    sprite[nSprite].extra = -1;

//	GrabTimeSlot(3);

    WaspList[nWasp].nAction = 0;
    WaspList[nWasp].nFrame  = 0;
    WaspList[nWasp].nSprite = nSprite;
    WaspList[nWasp].nTarget = -1;
    WaspList[nWasp].nHealth = 800;
    WaspList[nWasp].nDamage = 10;

    if (bEggWasp)
    {
        WaspList[nWasp].field_C = 60;
        WaspList[nWasp].nDamage /= 2;
    }
    else
    {
        WaspList[nWasp].field_C = RandomSize(5);
    }

    WaspList[nWasp].nAttackTime = 0;
    WaspList[nWasp].field_14 = 0;
    WaspList[nWasp].nVelocity = 0;
    WaspList[nWasp].dTime = RandomSize(7) + 127;

    sprite[nSprite].owner = runlist_AddRunRec(sprite[nSprite].lotag - 1, nWasp, kRunWasp);

    WaspList[nWasp].nRun = runlist_AddRunRec(NewRun, nWasp, kRunWasp);

    nCreaturesLeft++;
    return nSprite;
}

void FuncWasp(int a, int nDamage, int nRun)
{
    short nWasp = RunData[nRun].nVal;
    short nSprite = WaspList[nWasp].nSprite;
    short nAction = WaspList[nWasp].nAction;

    short nTarget = -1;

    bool bVal = false;

    int nMessage = a & kMessageMask;

    switch (nMessage)
    {
        case 0x90000:
        {
            seq_PlotSequence(a & 0xFFFF, SeqOffsets[kSeqWasp] + ActionSeq[nAction].a, WaspList[nWasp].nFrame, ActionSeq[nAction].b);
            return;
        }

        case 0xA0000:
        {
            if (!(sprite[nSprite].cstat & 0x101))
                return;

            nDamage = runlist_CheckRadialDamage(nSprite);
            // fall through to case 0x80000
            fallthrough__;
        }

        case 0x80000:
        {
            if (!nDamage) {
                return;
            }

            if (WaspList[nWasp].nHealth > 0)
            {
                WaspList[nWasp].nHealth -= nDamage;

                if (WaspList[nWasp].nHealth > 0)
                {
                    if (!RandomSize(4))
                    {
                        WaspList[nWasp].nAction = 3;
                        WaspList[nWasp].nFrame  = 0;
                    }

                    WaspList[nWasp].nAction = 1;
                    sprite[nSprite].ang += RandomSize(9) + 768;
                    sprite[nSprite].ang &= kAngleMask;

                    WaspList[nWasp].nVelocity = 3000;

                    sprite[nSprite].zvel = (-20) - RandomSize(6);
                }
                else
                {
                    // Wasp is dead
                    nVelShift = 0;

                    WaspList[nWasp].nAction = 4;
                    WaspList[nWasp].nFrame  = 0;

                    sprite[nSprite].cstat = 0;
                    sprite[nSprite].ang = (sprite[nSprite].ang + 1024) & kAngleMask;

                    SetWaspVel(nSprite);

                    sprite[nSprite].zvel = 512;

                    nCreaturesLeft--;
                }
            }
            return;
        }

        case 0x20000:
        {
            short nSeq = SeqOffsets[kSeqWasp] + ActionSeq[nAction].a;

            sprite[nSprite].picnum = seq_GetSeqPicnum2(nSeq, WaspList[nWasp].nFrame);

            seq_MoveSequence(nSprite, nSeq, WaspList[nWasp].nFrame);

            WaspList[nWasp].nFrame++;
            if (WaspList[nWasp].nFrame >= SeqSize[nSeq])
            {
                WaspList[nWasp].nFrame = 0;
                bVal = true;
            }

            if (WaspList[nWasp].nHealth > 0)
            {
                nTarget = WaspList[nWasp].nTarget;

                if (nTarget > -1 && (!(sprite[nTarget].cstat & 0x101) || (SectFlag[sprite[nTarget].sectnum] & kSectUnderwater)))
                {
                    // goto pink
                    WaspList[nWasp].nTarget = -1;
                    WaspList[nWasp].nAction = 0;
                    WaspList[nWasp].field_C = RandomSize(6);
                    return;
                }
            }

            switch (nAction)
            {
                default:
                    return;

                case 0:
                {
                    sprite[nSprite].zvel = Sin(WaspList[nWasp].nAttackTime) >> 4;

                    WaspList[nWasp].nAttackTime += WaspList[nWasp].dTime;
                    WaspList[nWasp].nAttackTime &= kAngleMask;

                    MoveCreature(nSprite);

                    if (nTarget >= 0)
                    {
                        WaspList[nWasp].field_C--;
                        if (WaspList[nWasp].field_C > 0)
                        {
                            PlotCourseToSprite(nSprite, nTarget);
                        }
                        else
                        {
                            sprite[nSprite].zvel = 0;
                            WaspList[nWasp].nAction = 1;
                            WaspList[nWasp].nFrame  = 0;
                            WaspList[nWasp].nVelocity = 1500;
                            WaspList[nWasp].field_C = RandomSize(5) + 60;
                        }
                    }
                    else
                    {
                        if ((nWasp & 0x1F) == (totalmoves & 0x1F)) {
                            WaspList[nWasp].nTarget = FindPlayer(nSprite, 60);
                        }
                    }

                    return;
                }

                case 1:
                {
                    WaspList[nWasp].field_C--;

                    if (WaspList[nWasp].field_C <= 0)
                    {
                        WaspList[nWasp].nAction = 0;
                        WaspList[nWasp].field_C = RandomSize(6);
                        return;
                    }

                    int nChaseVal = AngleChase(nSprite, nTarget, WaspList[nWasp].nVelocity, 0, 16);

                    switch (nChaseVal & 0xC000)
                    {
                        default:
                            return;

                        case 0x8000:
                        {
                            return;
                        }

                        case 0xC000:
                        {
                            short nSprite2 = (nChaseVal & 0x3FFF);
                            if (nSprite2 == nTarget)
                            {
                                sprite[nSprite].xvel = 0;
                                sprite[nSprite].yvel = 0;
                                runlist_DamageEnemy(nSprite2, nSprite, WaspList[nWasp].nDamage);
                                WaspList[nWasp].nAction = 2;
                                WaspList[nWasp].nFrame = 0;
                            }
                            return;
                        }
                    }

                    return;
                }

                case 2:
                case 3:
                {
                    if (bVal)
                    {
                        sprite[nSprite].ang += RandomSize(9) + 768;
                        sprite[nSprite].ang &= kAngleMask;
                        sprite[nSprite].zvel = (-20) - RandomSize(6);

                        WaspList[nWasp].nAction = 1;
                        WaspList[nWasp].nVelocity = 3000;
                    }
                    return;
                }
                case 4:
                {
                    int nMove = MoveCreature(nSprite) & 0x8000;
                    nMove |= 0xC000;

                    if (nMove)
                    {
                        sprite[nSprite].xvel = 0;
                        sprite[nSprite].yvel = 0;
                        sprite[nSprite].zvel = 1024;
                        WaspList[nWasp].nAction = 5;
                        WaspList[nWasp].nFrame = 0;
                    }

                    return;
                }
                case 5:
                {
                    short nSector = sprite[nSprite].sectnum;

                    sprite[nSprite].z += sprite[nSprite].zvel;

                    if (sprite[nSprite].z >= sector[nSector].floorz)
                    {
                        if (SectBelow[nSector] > -1)
                        {
                            BuildSplash(nSprite, nSector);
                            sprite[nSprite].cstat |= 0x8000;
                        }

                        sprite[nSprite].xvel = 0;
                        sprite[nSprite].yvel = 0;
                        sprite[nSprite].zvel = 0;
                        WaspList[nWasp].nAction = 6;
                        WaspList[nWasp].nFrame = 0;
                        runlist_SubRunRec(WaspList[nWasp].nRun);
                    }

                    return;
                }
            }

            break;
        }
    }
}

class WaspLoadSave : public LoadSave
{
public:
    virtual void Load();
    virtual void Save();
};

void WaspLoadSave::Load()
{
    Read(&nVelShift, sizeof(nVelShift));
    Read(&nWaspCount, sizeof(nWaspCount));
    Read(WaspList, sizeof(WaspList[0]) * nWaspCount);
}

void WaspLoadSave::Save()
{
    Write(&nVelShift, sizeof(nVelShift));
    Write(&nWaspCount, sizeof(nWaspCount));
    Write(WaspList, sizeof(WaspList[0]) * nWaspCount);
}

static WaspLoadSave* myLoadSave;

void WaspLoadSaveConstruct()
{
    myLoadSave = new WaspLoadSave();
}


code:
//-------------------------------------------------------------------------
/*
Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 sirlemonhead, Nuke.YKT

This file is part of PCExhumed.

PCExhumed is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/
//-------------------------------------------------------------------------

#ifndef __wasp_h__
#define __wasp_h__

#include "aistuff.h"

extern short nWaspCount;

void InitWasps();
int GetWaspSprite(int nWasp);
int BuildWasp(short nSprite, int x, int y, int z, short nSector, short nAngle);
void FuncWasp(int eax, int edx, int nRun);

#endif


code:
//-------------------------------------------------------------------------
/*
Copyright (C) 2010 EDuke32 developers and contributors

This file is part of EDuke32.

EDuke32 is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/
//-------------------------------------------------------------------------

#ifdef _WIN32

#include "compat.h"

#define NEED_SHELLAPI_H
#define NEED_WINSOCK2_H
#define NEED_WS2TCPIP_H
#include "windows_inc.h"

#include "renderlayer.h"

int32_t G_GetVersionFromWebsite(char *buffer)
{
    static int32_t wsainitialized = 0;
    int32_t i=0, j=0, r=0;
    struct sockaddr_in dest_addr;
    struct hostent *h;
    char const *host = "www.eduke32.com";
    char const *req = "GET http://www.eduke32.com/VERSION HTTP/1.0\r\n\r\n\r\n";
    char *tok;
    char tempbuf[2048],otherbuf[16],ver[16];
    SOCKET mysock;
    WSADATA ws;

#ifdef _WIN32
    if (wsainitialized == 0)
    {
        if (WSAStartup(0x101, &ws) == SOCKET_ERROR)
            return 0;

        wsainitialized = 1;
    }
#endif

    if ((h = gethostbyname(host)) == NULL)
    {
        initprintf("Couldn't resolve %s!\n", host);
        return 0;
    }

    dest_addr.sin_addr.s_addr = ((struct in_addr *)(h->h_addr))->s_addr;
    dest_addr.sin_family = AF_INET;
    dest_addr.sin_port = htons(80);

    memset(&(dest_addr.sin_zero), '\0', 8);

    mysock = socket(PF_INET, SOCK_STREAM, 0);

    if (mysock == INVALID_SOCKET)
    {
        WSACleanup();
        return 0;
    }

    initprintf("Connecting to http://%s\n",host);

    if (connect(mysock, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr)) == SOCKET_ERROR)
        goto done;

    i = send(mysock, req, strlen(req), 0);

    if (i == SOCKET_ERROR)
        goto done;

    i = recv(mysock, (char *)&tempbuf, sizeof(tempbuf), 0);

    if (i < 0)
        goto done;

    Bmemcpy(&otherbuf, &tempbuf, sizeof(otherbuf));

    strtok(otherbuf, " ");

    if ((tok = strtok(NULL, " ")) == NULL)
        goto done;

    if (Batol(tok) == 200)
    {
        for (i = 0; (unsigned)i < strlen(tempbuf); i++)  // HACK: all of this needs to die a fiery death; we just skip to the content
        {
            // instead of actually parsing any of the http headers
            if (i > 4)
                if (tempbuf[i-1] == '\n' && tempbuf[i-2] == '\r' && tempbuf[i-3] == '\n' && tempbuf[i-4] == '\r')
                {
                    while (j < 9)
                    {
                        ver[j] = tempbuf[i];
                        i++, j++;
                    }
                    ver[j] = '\0';
                    break;
                }
        }

        if (j)
        {
            strcpy(buffer, ver);
            r = 1;
            goto done;
        }
    }

done:
    closesocket(mysock);
    WSACleanup();

    return r;
}
#endif


code:
#ifndef __khrplatform_h_
#define __khrplatform_h_

/*
** Copyright (c) 2008-2018 The Khronos Group Inc.
**
** Permission is hereby granted, free of charge, to any person obtaining a
** copy of this software and/or associated documentation files (the
** "Materials"), to deal in the Materials without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Materials, and to
** permit persons to whom the Materials are furnished to do so, subject to
** the following conditions:
**
** The above copyright notice and this permission notice shall be included
** in all copies or substantial portions of the Materials.
**
** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
*/

/* Khronos platform-specific types and definitions.
 *
 * The master copy of khrplatform.h is maintained in the Khronos EGL
 * Registry repository at https://github.com/KhronosGroup/EGL-Registry
 * The last semantic modification to khrplatform.h was at commit ID:
 *      67a3e0864c2d75ea5287b9f3d2eb74a745936692
 *
 * Adopters may modify this file to suit their platform. Adopters are
 * encouraged to submit platform specific modifications to the Khronos
 * group so that they can be included in future versions of this file.
 * Please submit changes by filing pull requests or issues on
 * the EGL Registry repository linked above.
 *
 *
 * See the Implementer's Guidelines for information about where this file
 * should be located on your system and for more details of its use:
 *    http://www.khronos.org/registry/implementers_guide.pdf
 *
 * This file should be included as
 *        #include <KHR/khrplatform.h>
 * by Khronos client API header files that use its types and defines.
 *
 * The types in khrplatform.h should only be used to define API-specific types.
 *
 * Types defined in khrplatform.h:
 *    khronos_int8_t              signed   8  bit
 *    khronos_uint8_t             unsigned 8  bit
 *    khronos_int16_t             signed   16 bit
 *    khronos_uint16_t            unsigned 16 bit
 *    khronos_int32_t             signed   32 bit
 *    khronos_uint32_t            unsigned 32 bit
 *    khronos_int64_t             signed   64 bit
 *    khronos_uint64_t            unsigned 64 bit
 *    khronos_intptr_t            signed   same number of bits as a pointer
 *    khronos_uintptr_t           unsigned same number of bits as a pointer
 *    khronos_ssize_t             signed   size
 *    khronos_usize_t             unsigned size
 *    khronos_float_t             signed   32 bit floating point
 *    khronos_time_ns_t           unsigned 64 bit time in nanoseconds
 *    khronos_utime_nanoseconds_t unsigned time interval or absolute time in
 *                                         nanoseconds
 *    khronos_stime_nanoseconds_t signed time interval in nanoseconds
 *    khronos_boolean_enum_t      enumerated boolean type. This should
 *      only be used as a base type when a client API's boolean type is
 *      an enum. Client APIs which use an integer or other type for
 *      booleans cannot use this as the base type for their boolean.
 *
 * Tokens defined in khrplatform.h:
 *
 *    KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values.
 *
 *    KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0.
 *    KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0.
 *
 * Calling convention macros defined in this file:
 *    KHRONOS_APICALL
 *    KHRONOS_APIENTRY
 *    KHRONOS_APIATTRIBUTES
 *
 * These may be used in function prototypes as:
 *
 *      KHRONOS_APICALL void KHRONOS_APIENTRY funcname(
 *                                  int arg1,
 *                                  int arg2) KHRONOS_APIATTRIBUTES;
 */

#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC)
#   define KHRONOS_STATIC 1
#endif

/*-------------------------------------------------------------------------
 * Definition of KHRONOS_APICALL
 *-------------------------------------------------------------------------
 * This precedes the return type of the function in the function prototype.
 */
#if defined(KHRONOS_STATIC)
    /* If the preprocessor constant KHRONOS_STATIC is defined, make the
     * header compatible with static linking. */
#   define KHRONOS_APICALL
#elif defined(_WIN32)
#   define KHRONOS_APICALL __declspec(dllimport)
#elif defined (__SYMBIAN32__)
#   define KHRONOS_APICALL IMPORT_C
#elif defined(__ANDROID__)
#   define KHRONOS_APICALL __attribute__((visibility("default")))
#else
#   define KHRONOS_APICALL
#endif

/*-------------------------------------------------------------------------
 * Definition of KHRONOS_APIENTRY
 *-------------------------------------------------------------------------
 * This follows the return type of the function  and precedes the function
 * name in the function prototype.
 */
#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__)
    /* Win32 but not WinCE */
#   define KHRONOS_APIENTRY __stdcall
#else
#   define KHRONOS_APIENTRY
#endif

/*-------------------------------------------------------------------------
 * Definition of KHRONOS_APIATTRIBUTES
 *-------------------------------------------------------------------------
 * This follows the closing parenthesis of the function prototype arguments.
 */
#if defined (__ARMCC_2__)
#define KHRONOS_APIATTRIBUTES __softfp
#else
#define KHRONOS_APIATTRIBUTES
#endif

/*-------------------------------------------------------------------------
 * basic type definitions
 *-----------------------------------------------------------------------*/
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)

/*
 * Using <stdint.h>
 */
#include <stdint.h>
typedef int32_t                 khronos_int32_t;
typedef uint32_t                khronos_uint32_t;
typedef int64_t                 khronos_int64_t;
typedef uint64_t                khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64   1
#define KHRONOS_SUPPORT_FLOAT   1

#elif defined(__VMS ) || defined(__sgi)

/*
 * Using <inttypes.h>
 */
#include <inttypes.h>
typedef int32_t                 khronos_int32_t;
typedef uint32_t                khronos_uint32_t;
typedef int64_t                 khronos_int64_t;
typedef uint64_t                khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64   1
#define KHRONOS_SUPPORT_FLOAT   1

#elif defined(_WIN32) && !defined(__SCITECH_SNAP__)

/*
 * Win32
 */
typedef __int32                 khronos_int32_t;
typedef unsigned __int32        khronos_uint32_t;
typedef __int64                 khronos_int64_t;
typedef unsigned __int64        khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64   1
#define KHRONOS_SUPPORT_FLOAT   1

#elif defined(__sun__) || defined(__digital__)

/*
 * Sun or Digital
 */
typedef int                     khronos_int32_t;
typedef unsigned int            khronos_uint32_t;
#if defined(__arch64__) || defined(_LP64)
typedef long int                khronos_int64_t;
typedef unsigned long int       khronos_uint64_t;
#else
typedef long long int           khronos_int64_t;
typedef unsigned long long int  khronos_uint64_t;
#endif /* __arch64__ */
#define KHRONOS_SUPPORT_INT64   1
#define KHRONOS_SUPPORT_FLOAT   1

#elif 0

/*
 * Hypothetical platform with no float or int64 support
 */
typedef int                     khronos_int32_t;
typedef unsigned int            khronos_uint32_t;
#define KHRONOS_SUPPORT_INT64   0
#define KHRONOS_SUPPORT_FLOAT   0

#else

/*
 * Generic fallback
 */
#include <stdint.h>
typedef int32_t                 khronos_int32_t;
typedef uint32_t                khronos_uint32_t;
typedef int64_t                 khronos_int64_t;
typedef uint64_t                khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64   1
#define KHRONOS_SUPPORT_FLOAT   1

#endif

/*
 * Types that are (so far) the same on all platforms
 */
typedef signed   char          khronos_int8_t;
typedef unsigned char          khronos_uint8_t;
typedef signed   short int     khronos_int16_t;
typedef unsigned short int     khronos_uint16_t;

/*
 * Types that differ between LLP64 and LP64 architectures - in LLP64,
 * pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
 * to be the only LLP64 architecture in current use.
 */
#ifdef _WIN64
typedef signed   long long int khronos_intptr_t;
typedef unsigned long long int khronos_uintptr_t;
typedef signed   long long int khronos_ssize_t;
typedef unsigned long long int khronos_usize_t;
#else
typedef signed   long  int     khronos_intptr_t;
typedef unsigned long  int     khronos_uintptr_t;
typedef signed   long  int     khronos_ssize_t;
typedef unsigned long  int     khronos_usize_t;
#endif

#if KHRONOS_SUPPORT_FLOAT
/*
 * Float type
 */
typedef          float         khronos_float_t;
#endif

#if KHRONOS_SUPPORT_INT64
/* Time types
 *
 * These types can be used to represent a time interval in nanoseconds or
 * an absolute Unadjusted System Time.  Unadjusted System Time is the number
 * of nanoseconds since some arbitrary system event (e.g. since the last
 * time the system booted).  The Unadjusted System Time is an unsigned
 * 64 bit value that wraps back to 0 every 584 years.  Time intervals
 * may be either signed or unsigned.
 */
typedef khronos_uint64_t       khronos_utime_nanoseconds_t;
typedef khronos_int64_t        khronos_stime_nanoseconds_t;
#endif

/*
 * Dummy value used to pad enum types to 32 bits.
 */
#ifndef KHRONOS_MAX_ENUM
#define KHRONOS_MAX_ENUM 0x7FFFFFFF
#endif

/*
 * Enumerated boolean type
 *
 * Values other than zero should be considered to be true.  Therefore
 * comparisons should not be made against KHRONOS_TRUE.
 */
typedef enum {
    KHRONOS_FALSE = 0,
    KHRONOS_TRUE  = 1,
    KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM
} khronos_boolean_enum_t;

#endif /* __khrplatform_h_ */