view 3x666.c @ 26:7f7330567277

Silence some build warnings.
author Matti Hamalainen <ccr@tnsp.org>
date Fri, 15 Mar 2013 23:36:46 +0200
parents f896fe6a98da
children 3f8f518905db
line wrap: on
line source

#include <SDL.h>
#include <math.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdarg.h>
#include "config.h"
#include "3xfont.h"


#if !defined(FALSE) && !defined(TRUE) && !defined(BOOL)
typedef enum { FALSE = 0, TRUE = 1 } BOOL;
#endif

#ifndef BOOL
#    ifdef bool
#        define BOOL bool
#    else
#        define BOOL int
#    endif
#endif


struct
{
    int tickLen;
    int frameTime, frameCount,
        startTime, endTime;

    BOOL exitFlag;

    int mixFill, mixBufSize;
    Sint32 *mixBuf;

    int optVFlags;
    SDL_Surface *screen;
    SDL_Event event;
    SDL_AudioSpec optAfmt;
} engine;



typedef struct
{
    int x, y, z;
} vec3d;


int *ballz;

void setpal(void);


static inline int dmClamp(const int v, const int min, const int max)
{
    return (v < min ? min : (v > max ? max : v));
}


static int engineGetTick()
{
    return ((engine.frameTime - engine.startTime) * SET_DEMOHZ) / 1000;
}


void dmPrintVA(const char *fmt, va_list ap)
{
#ifdef __WIN32
    (void) fmt;
    (void) ap;
#else
    vfprintf(stderr, fmt, ap);
#endif
}


void dmPrint(const char *fmt, ...)
{
#ifdef __WIN32
    (void) fmt;
#else
    va_list ap;

    va_start(ap, fmt);
    dmPrintVA(fmt, ap);
    va_end(ap);
#endif
}


void dmErrorVA(const char *fmt, va_list ap)
{
    fprintf(stderr, SET_PROG_NAME ": ");
    vfprintf(stderr, fmt, ap);
}


void dmError(const char *fmt, ...)
{
    va_list ap;

    va_start(ap, fmt);
    dmErrorVA(fmt, ap);
    va_end(ap);
}



    
static BOOL engineInitializeVideo()
{
    SDL_FreeSurface(engine.screen);

    engine.screen = SDL_SetVideoMode(SET_VID_BUFW, SET_VID_BUFH, 8, engine.optVFlags);
    if (engine.screen == NULL)
    {
        dmError("Can't SDL_SetVideoMode(): %s\n", SDL_GetError());
        return FALSE;
    }

    setpal();
    return TRUE;
}


/**************** tEXT dRAWiNG rOUTiNES **********

bitmaps are for lamers :) let's use a little 12-segment calculator font...
ascii chars 32..90, 16 bits per char unpacked -> 114 bytes for the whole
font ;) let's credit karl/nooon for the original idea. */

void txtDrawSegment(int y, int x, int w, int h)
{
    /* clip clip clip */
    if (x + w > SET_VID_BUFW)
        w = SET_VID_BUFW - x;
    if (x < 0)
    {
        w += x;
        x = 0;
    }
    if (y + h > SET_VID_BUFH)
        h = SET_VID_BUFH - y;
    if (y < 0)
    {
        h += y;
        y = 0;
    }
    if (w > 0 && h > 0)
    {
        Uint8 *b = engine.screen->pixels + (y * engine.screen->pitch) + x;
        for (; h; h--)
        {
            memset(b, 122, w);
            b += engine.screen->pitch;
        }
    }
}

void txtDrawChar(int x, int y, int c, int xunit, int yunit)
{
    x -= xunit * 2;
    y -= yunit * 3;
    for (;;)
    {
        if (!c)
            break;

        if (c & 1)
            txtDrawSegment(y, x + 1, xunit * 2 - 2, yunit);
        if (c & 2)
            txtDrawSegment(y, x + 1 + xunit * 2, xunit * 2 - 2, yunit);
        y++;

        c >>= 2;
        if (!c)
            break;

        if (c & 1)
            txtDrawSegment(y, x, xunit, yunit * 3 - 2);
        if (c & 2)
            txtDrawSegment(y, x + ((xunit * 3) >> 1), xunit, yunit * 3 - 2);
        if (c & 4)
            txtDrawSegment(y, x + xunit * 3, xunit, yunit * 3 - 2);

        y += yunit * 2;
        c >>= 3;
    }
}

void drawtxtscr(char *str)
{
    int x = SET_VID_BUFW >> 4, y = SET_VID_BUFH >> 3;
    while (*str)
    {
        if (*str >= 32)
        {
            txtDrawChar(x, y, phont[*str - 32], SET_VID_BUFW / 50, SET_VID_BUFW / 80);
            x += SET_VID_BUFW / 10;
        }
        else
        {
            x = SET_VID_BUFW >> 4;
            y += SET_VID_BUFW / 10;
        }
        str++;
    }
}


void flashtxt(char *str)
{
    int x = (SET_VID_BUFW >> 1) - (strlen(str) + 1) * 3 * SET_VID_BUFW / 80;
    while (*str)
    {
        txtDrawChar(x, SET_VID_BUFH >> 1, phont[*str++ - 32], SET_VID_BUFW / 50, SET_VID_BUFW / 80);
        x += SET_VID_BUFW / 10;
    }
}

/*************************** DA PHONGBALL HEAVEN **************

A short course on phongball theory!

A sphere: x^2+y^2+z^2=R^2

Diffuse shading: intensity = dotproduct(surfacenormal,lightvector)

(doing this for every drawn point of the surface is sometimes called
phong shading even if the normals aren't actually interpolated)

For a sphere, a normal vector at a point of the surface == constant *
the coordinates of the point (if center == origo).

Thus, the function for the intensity of a 2d-projected phongball can be
stated as

intensity(x,y) = l.x*x + l.y*y + l.z*z, z = sqrt(R^2-x^2-y^2)

The first two muls can be eliminated easily. (and will be eliminated by
a good compiler even if you are lazy)

The third mul can be eliminated by approximating l.z*z with a function
of the form f(x)=a*x^2+c. This approximation makes the ball look a bit
"twisty" but who cares, it just looks damn cool ;)

***/

#if (SET_VID_BUFH < SET_VID_BUFW)
#    define maxR (SET_VID_BUFH >> 1)
#else
#    define maxR (SET_VID_BUFW >> 1)
#endif

struct
{
    int *tab;
    signed int R;
} balltab[50];

void preball()
{
    unsigned int rR;
    signed int R;

    for (rR = 0; rR < 48; rR++)
    {
        int R2, *d;
        signed int y;
        R = (maxR * (rR + 4)) / 52;
        if (R < 2)
            R = 2;
        R2 = R * R;
        balltab[rR].R = R;
        d = balltab[rR].tab = malloc(R * 2 * sizeof(int));
        for (y = -R + 1; y < R - 1; y++)
            *d++ = sqrt(R2 - y * y);
    }

    balltab[49].R = balltab[48].R = balltab[47].R;
    balltab[49].tab = balltab[48].tab = balltab[47].tab;
}


void drawball_inloop(Uint8 *d, int dotxyz, int ddot, int dddot, int x)
{
    for (; x; x--)
    {
        dotxyz += ddot;
        ddot += dddot;
        *d++ = dotxyz >> 16;
    }
}

void drawball(SDL_Surface *screen, vec3d * l, int relR)
{
    int R = balltab[relR].R, *s = balltab[relR].tab;

    signed int doty = (-(R - 1) * l->y);
    signed int y = R * 2 - 2;

    Uint8 *dp = screen->pixels + (SET_VID_BUFW >> 1) - R + ((SET_VID_BUFH >> 1) - R) * screen->pitch;

    for (; y; y--)
    {
        int halfw = *s++;
        if (halfw)
        {
            drawball_inloop(dp + R - halfw,
                            (doty - (l->x * halfw)) << 8,
                            (l->x + l->z) << 8,
                            0 - ((l->z << 8) / halfw), halfw << 1);
        }
        dp += screen->pitch;
        doty += l->y;
    }
}

/* some extra for freaks: a plasma made with the phongball innerloop :)
   looks ugly.

void drawplasma(char *d,float t)
{
	int y=SET_VID_BUFH; float u=0,du=500/SET_VID_BUFH;

	for(;y;y--){
		drawball_inloop(d,
			sin(t*0.02+0+u*0.033)*65536*256,
			cos(t*0.04+1+u*0.022)*65536*4096/SET_VID_BUFW,
			-2*cos(t*0.04+1+u*0.022)*65536*4096/(SET_VID_BUFW*SET_VID_BUFW), SET_VID_BUFW);
		d+=SET_VID_BUFW;
		u+=du;
	}
}
*/

/************************ oTHA FX ***************/

void rotochess(SDL_Surface *screen, int du, int dv, int iu, int iv)
{
    int hu = iu - (dv * (SET_VID_BUFH >> 1)) - (du * (SET_VID_BUFW >> 1)),
        hv = iv + (du * (SET_VID_BUFH >> 1)) - (dv * (SET_VID_BUFW >> 1));

    int y;
    Uint8 *dp = screen->pixels;

    for (y = SET_VID_BUFH; y; y--)
    {
        Uint8 *d = dp;
        int u = hu, v = hv, x;
        for (x = SET_VID_BUFW; x; x--)
        {
            u += du;
            v += dv;
            *d++ = ((u ^ v) >> 8) & 0xb1;
        }
        dp += screen->pitch;
        hu += dv;
        hv -= du;
    }
}

/***************************************************************/


void setpal(void)
{
    SDL_Color pal[SET_VID_COLORS];
    int i, a = 3, b = 0;

    for (i = SET_VID_COLORS - 1; i >= 0; i--)
    {
        int n = (i + 128) & 255;
        pal[n].r =  (abs(i - 140) >> a) & 255;
        pal[n].g = ((abs(i - 128) >> b) & 255) ^ 1;
        pal[n].b =  (abs(i - 96) >> b) & 255;
        if (i == 128)
        {
            a = 0;
            b = 1;
        }
    }

    SDL_SetColors(engine.screen, pal, 0, SET_VID_COLORS);
}


void unitvec(vec3d * v, float a, float b, float c, float m)
{
    float cam = cos(a) * m, sam = sin(a) * m, sbcam = sin(b) * cam;

    v->x = cos(b) * cam;
    v->y = cos(c) * sam - sin(c) * sbcam;
    v->z = cos(c) * sbcam + sin(c) * sam;
}

/************************* MUSiC cODE **************************/

/* This table was ripped (and reduced and rudely integerized) from the
   Maube tracker by K .. keep on the good work man! ;) */

const Sint16 noterate[3 * 12] =
{
    1000, 1059, 1122, 1189, 1259, 1334,
    1414, 1498, 1587, 1681, 1781, 1887,

    2000, 2118, 2244, 2378, 2519, 2669,
    2828, 2996, 3174, 3363, 3563, 3775,

    4000, 4237, 4489, 4756, 5039, 5339,
    5656, 5993, 6349, 6727, 7127, 7550
};

/*	64 bytes of pure musical power ;)
	Originally composed with Scream Tracker.  */

const char basstrak[32] =
{
    12, 0, 24, 12,
    12, 24, 12, 24,
    12, 0, 24, 12,
    12, 24, 12, 24,
    15, 0, 27, 15,
    15, 27, 15, 27,
    14, 0, 26, 14,
    15, 27, 17, 29
};

const char melody[32] =
{
    24, 12, 19, 15,
    24, 0, 19, 0,
    24, 12, 19, 15,
    24, 0, 15, 19,
    15, 19, 15, 19,
    22, 0, 15, 19,
    14, 17, 21, 14,
    22, 17, 17, 22
};

int *drum0 = NULL,
    *drum1 = NULL;


/* sampling sucks! */
void audio_precalcs()
{
    int drumlgt = engine.tickLen * SET_ROWTIX * 4;
    int *d = drum0 = malloc(drumlgt * sizeof(int)),
        *e = drum1 = malloc(drumlgt * sizeof(int)), i,
        vol = 24680, dvol = 35000 / (float) drumlgt;
    int o = 0, oo = 0;
    float a = 0, da = 386 / (float) drumlgt, dda = da / (float) drumlgt;

    dmPrint("aCtIvATiNg 303 eMuLAtOR\n");

    for (i = drumlgt; i; i--)
    {
        o = (o >> 1) + (rand() % vol) - (rand() % vol);
        oo = (oo * 2 + ((rand() % vol) - (rand() % vol))) / 3;

        o *= sin(a);
        oo *= sin(a);

        *d++ = (vol * sin((a / 2) + ((float) dmClamp(o * 2, -65535, 65535)) / 80000));
        *e++ = (vol * sin(a + ((float) oo) / 60000));

        a += da;
        da -= dda;
        vol -= dvol;
    }
}


void audio_close()
{
    if (drum0) free(drum0);
    if (drum1) free(drum1);
}

static int engineRenderAudio(Sint32 *stream)
{
    static int rowno = 0;
    static signed int delta = -5;
    static char ismelody = 0, silend = 0;
    int i, rowlgt = engine.tickLen * SET_ROWTIX;
    Sint32 *d = stream, note;

    /* BASS (sawtooth ofcoz) */
    note = basstrak[(rowno >> 1) & 31];

    if (!note)
        note = basstrak[((rowno >> 1) & 31) - 1];
    else
    if (rowno & 1)
        note = 0;
    
    if ((rowno & 3) == 3)
        note = 0;
    
    if (note)
    {
        int ps = 16384, dps;
        note += delta;
        dps = ((noterate[note] << 10) / SET_AUDIO_FREQ);
        for (i = rowlgt; i; i--)
        {
            *d++ = ps;
            ps = (ps + dps) & 32767;
        }
    }
    else
    {
        for (i = rowlgt; i; i--)
            *d++ = 16384;
    }

    /* MELODY (sawtooth as well :) */
    if (!(silend && ((rowno & 63) > 47)))
    {
        if (ismelody)
        {
            d = stream;
            if (rowno & 1)
                note = 0;
            else
                note = melody[(rowno >> 1) & 31];
            if (note)
            {
                int ps = 16384, dps;    /* this loop is different */
                note += delta;
                dps = ((noterate[note] << 12) / engine.optAfmt.freq);
                for (i = rowlgt; i; i--)
                {
                    *d++ += ps;
                    ps = (ps + dps) & 32767;
                }
            }
        }

        /* DRUMS (rave on!!!) */
        {
            int *s = drum1;
            d = stream;
            if (rowno & 4)
                s = drum0;
            s += (rowno & 3) * rowlgt;

            for (i = rowlgt; i; i--)
                *d++ += *s++;
        }
    }

    /* PATTERN SHIFT */
    rowno++;

    /* no switch+case? just check out how gcc handles them!
       it's 1024+ bytes for every phukken switch statement!
       in this case we can prefer size to speed, can't we? */

    if ((rowno & 63) == 0)
    {
        int r = rowno >> 6;
        if (r == 2)
            delta = 0;
        if (r == 4)
            ismelody = 1;
        if (r == 6 || r == 10)
            delta = 5;
        if (r == 7 || r == 11)
            silend = 1;
        if (r == 8)
            delta = silend = 0;
        if (r == 12)
        {
            rowno = ismelody = silend = 0;
            delta = -5;
        }
    }

    return rowlgt;
}


static void engineAudioCallback(void *userdata, Uint8 *stream, int len)
{
    (void) userdata;
    // We inherently assume mono here, as the audio code is not
    // capable of producing stereo anyway (for now, at least.)
    int i, need = len / sizeof(Sint16);

#if 0
    // The audio rendering handling is a bit silly due to the way
    // the original code works (assumes that it can always render
    // a certain amount/duration of data, instead of being given
    // the buffer duration/size to render. So we cope with that here.
    if (need >= engine.mixBufSize)
        need = engine.mixBufSize;
    
    while (engine.mixFill < need && engine.mixFill < engine.mixBufSize)
    {
        int got = engineRenderAudio(engine.mixBuf + engine.mixFill);
        engine.mixFill += got;
    }

    Sint16 *dp = (Sint16 *) stream;
    Sint32 *sp = engine.mixBuf;
    for (i = 0; i < need; i++)
        *dp++ = dmClamp(*sp++, -32767, 32767);

    engine.mixFill -= need;
    if (engine.mixFill > 0)
    {
        memmove(engine.mixBuf, engine.mixBuf + need, engine.mixFill * sizeof(Sint32));
    }
#endif
}


/**************** tEXT gENERATORS eTC ***************/

char skrtxt[] =
{
    "         HI THERE !  THIS IS THE FIRST OCSA RELEASE FOR LINUX !  "
        "IT'S A STUPID INTRO CALLED 3X666 !          "
};


#define CHTIME 16
#define CHPSCR 8

void plainscroll(int t)
{
    int chno = t / CHTIME;
    int x = 0 - ((t % CHTIME) * (SET_VID_BUFW / CHPSCR)) / CHTIME;
    int h = (abs((t % 48) - 24) * SET_VID_BUFH) / 256, i;
    char *c = skrtxt + chno;

    for (i = 0; i < CHPSCR + 1; i++)
    {
        txtDrawChar(x, (SET_VID_BUFH * 3) / 4, phont[*c++ - 32], SET_VID_BUFW / (6 * CHPSCR), h);
        x += SET_VID_BUFW / CHPSCR;
    }
}

char *lyrix(void)
{
    static int phinext = 0, philast;
    int phiwsty;
    char *phiword;

    phiwsty = phinext;

    if (!phiwsty)
    {
        if (!(rand() & 3))
            phiwsty = 13;
        else
            phiwsty = 1 + (rand() & 1);
    }
    if (phiwsty == 1)
    {
        char *w[] = { "HERE", "THERE", "NOW", "TOMORROW", "TODAY", "NEVER" };
        phiword = w[rand() % 6];
        if (rand() & 1)
            phinext = 2;
        else
            phinext = 12;
    }
    else
    if (phiwsty == 2)
    {
        char nx[] = { 5, 10, 7, 3, 11 };
        philast = rand() & 1;
        if (!philast)
            phiword = "YOU";
        else
            phiword = "I";
        phinext = nx[rand() % 5];
    }
    else
    if (phiwsty == 3)
    {
        char *w[] = { "DON'T", "CAN'T", "WON'T", "COULDN'T" };
        phiword = w[rand() % 4];
        phinext = 7 + (rand() & 4);
    }
    else
    if (phiwsty == 4)
    {
        if (rand() & 1)
            phiword = "YOU";
        else
            phiword = "ME";
        if (rand() & 1)
            phinext = 6;
        else if (rand() & 1)
            phinext = 0;
        else
            phinext = 11;
    }
    else
    if (phiwsty == 5)
    {
        if (philast)
            phiword = "AM";
        else
            phiword = "ARE";
        if (rand() & 1)
            phinext = 6;
        else
            phinext = 12 + (rand() & 1);
    }
    else
    if (phiwsty == 6)
    {
        char *w[] = {
            "FALLING", "THINKING", "DREAMING", "CRYING",
            "LYING", "REACHING", "BREATHING", "BURNING", "RUNNING"
        };
        phiword = w[rand() % 9];
        if (rand() & 1)
            phinext = 9;
        else if (rand() & 1)
            phinext = 0;
        else
            phinext = 13;
    }
    else
    if (phiwsty == 7)
    {
        char nx[] = { 8, 4, 12 };
        if (rand() & 1)
            phiword = "NEED";
        else
            phiword = "WANT";
        phinext = nx[rand() % 3];
    }
    else
    if (phiwsty == 8)
    {
        phiword = "TO";
        phinext = 11;
    }
    else
    if (phiwsty == 9)
    {
        char *w[] = { "DOWN", "OFF", "OUT", "UP", "ABOUT" };
        phiword = w[rand() % 5];
        if (rand() & 1)
            phinext = rand() & 4;
        else
            phinext = 12 + (rand() & 1);
    }
    else
    if (phiwsty == 10)
    {
        char *w[] = { "CAN", "COULD", "WOULD", "MAY", "MIGHT" };
        phiword = w[rand() % 5];
        if (rand() & 1)
            phinext = 11;
        else
            phinext = 12;
    }
    else
    if (phiwsty == 11)
    {
        char *w[] = { "SEE", "HEAR", "FEEL", "THINK" };
        phiword = w[rand() % 4];
        if (rand() & 1)
            phinext = 12;
        else
            phinext = rand() & 4;
    }
    else
    if (phiwsty == 12)
    {
        char *w[] = { "WHAT", "SOMETHING", "NOTHING", "THINGS", "WHATEVER" };
        phiword = w[rand() % 5];
        phinext = 2;
    }
    else
    if (phiwsty == 13)
    {
        phiword = "THE";
        phinext = 14;
    }
    else
    {
        char *w[] = { "WAY", "EYES", "WORLD", "ROBOT", "FREEDOM", "HATE" };
        phiword = w[rand() % 6];
        phinext = 0;
    }
    return phiword;
}

char *dotxtscr(void)
{
    static int cnt = 0;
    cnt++;
    if (cnt == 1)
        return "WHERES THE\n" "DESIGN?\n\n" "WHERES THE\n" "ATTITUDE?!";

    if (cnt == 2)
        return "NOTHING\n" "HAPPENED\n" "IN 1997";

    if (cnt == 3)
        return "PERHAPS\n" "IT IS TIME\n" "FOR A NEW\n" "PROPHECY?";

    if (cnt == 4)
        return "IN 1998,\n" "SCENE WILL\n" "DIE !!!!!";

    if (cnt == 5)
        return "PHONGBALLS\n" "WILL\n" "INVADE\n" "THE WORLD";

    if ((cnt == 6) || (cnt == 7))
        return "HALUU OLLA\n" "APPELSIINI";

    return NULL;
}

const char *endscroll =
    "THAT'S ALL\n"
    "FOLKS !\n"
    "\n"
    "ALL CODING +\n"
    "COMPOSING\n"
    "BY VIZNUT !\n"
    "\n"
    "WHAT A\n"
    "MARVELOUS\n"
    "PALETTE !\n"
    "WHAT A\n"
    "SUPERB TUNE !\n"
    "\n"
    "BUT IT'S ALL\n"
    "BELOW 10 KB\n"
    "AND RUNS\n"
    "SMOOTHLY ON\n"
    "A 386\n"
    "\n"
    "GREETINGS TO\n"
    "ALL THE\n"
    "LINUX SCENE !\n"
    "\n"
    "LET'S MAKE\n"
    "THIS WORLD A\n"
    "BETTER PLACE\n"
    "TO LIVE IN !\n"
    "\n"
    "THIS IS JUST\n"
    "A PIECE OF\n"
    "SHITTY CODE\n"
    "BUT IT'S ALL\n"
    "YOURS !\n"
    "\n"
    "RIP OFF\n"
    "EVERYTHING !\n"
    "USE IT FOR\n"
    "SOMETHING\n"
    "CREATIVE !\n"
    "\n"
    "\n\n\nOCSA 1998";


void doendscroll(int t)
{
    const char *s = endscroll;
    int y = SET_VID_BUFH - (SET_VID_BUFH * t / 512), x = SET_VID_BUFW / 24;

    while (*s)
    {
        if (*s < 32)
        {
            x = SET_VID_BUFW / 24;
            y += SET_VID_BUFH / 8;
        }
        else
        if (y >= 0 - (SET_VID_BUFH / 8) && y < SET_VID_BUFH)
        {
            txtDrawChar(x, y, phont[*s - 32], SET_VID_BUFW / 60, SET_VID_BUFH / 60);
            x += SET_VID_BUFW / 13;
        }
        s++;
    }
}

/********************** tHA kORE bEGiNS *********************/

#define BLACKBG   0x0001
#define FLASHBG   0x0002
#define OCSALOGO  0x0004
#define SCROLL0   0x0008
#define BALLIE    0x0010
#define BALLJUMPS 0x0020
#define COUNTAH   0x0040
#define CHESSBG   0x0080
#define PLASMABG  0x0100
#define FLASHTXT  0x0200
#define TXTSCR    0x0400
#define ENDSCR    0x0800
#define DEMOEND   0x1000

const Uint16 dezign[] = {
    0   , BLACKBG | OCSALOGO | SCROLL0,
    256 , FLASHBG | BALLIE | BALLJUMPS | COUNTAH,
    384 , BLACKBG | BALLIE | BALLJUMPS | COUNTAH | OCSALOGO,
    400 , BLACKBG | BALLIE | COUNTAH | OCSALOGO,
    416 , BLACKBG | BALLIE,
    448 , BLACKBG | BALLIE | TXTSCR,
    512 , CHESSBG | BALLIE | BALLJUMPS | TXTSCR,
    576 , CHESSBG | BALLIE | BALLJUMPS | TXTSCR,
    640 , CHESSBG | BALLIE | BALLJUMPS | TXTSCR,
    704 , CHESSBG | BALLIE | BALLJUMPS | TXTSCR,
    768 , FLASHBG | FLASHTXT,
    896 , FLASHBG | FLASHTXT | TXTSCR,
    962 , FLASHBG | FLASHTXT | TXTSCR | BALLIE | BALLJUMPS,
    1024, BLACKBG | BALLIE | ENDSCR,
    1152, CHESSBG | BALLIE | BALLJUMPS | ENDSCR,
    1344, FLASHBG | BALLIE | BALLJUMPS | ENDSCR,
    1536, DEMOEND
};


/* don't look at the rest of the code, it just sucks :) */

int main(int argc, char *argv[])
{
    BOOL initSDL = FALSE;
    const int U = SET_VID_BUFW / 40;
    const Uint16  *dez = dezign;
    int flagz = 0, flixtim = 0;
    char *phiword = NULL, *dizainword = NULL;

    memset(&engine, 0, sizeof(engine));

    dmPrint(
    SET_PROG_NAME " by Ocsa (PWP) (c) 1998\n"
    "libSDL port by ccr/TNSP^PWP (c) 2013\n");
    
    // Parse commandline options
//    engine.optVFlags |= SDL_FULLSCREEN;


    // Initialize SDL components
    dmPrint("Engine initializing ..\n");
    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER) != 0)
    {
        dmError("Could not initialize SDL: %s\n", SDL_GetError());
        goto error_exit;
    }
    initSDL = TRUE;

    // Initialize audio parts
    engine.optAfmt.freq     = SET_AUDIO_FREQ;
    engine.optAfmt.format   = AUDIO_S16SYS;
    engine.optAfmt.channels = SET_AUDIO_CHN;
    engine.optAfmt.samples  = engine.optAfmt.freq / 16;
    engine.optAfmt.callback = engineAudioCallback;
    
    // Initialize SDL audio
    if (SDL_OpenAudio(&engine.optAfmt, NULL) < 0)
    {
        dmError("Couldn't open SDL audio: %s\n", SDL_GetError());
    }

    // Initialize SDL video
    if (!engineInitializeVideo())
        goto error_exit;

    SDL_ShowCursor(SDL_DISABLE);
    SDL_WM_SetCaption(SET_WIN_NAME, SET_WIN_NAME);

    // Initialize effects
    preball();
    srand(0);

    engine.tickLen = engine.optAfmt.freq / SET_DEMOHZ;
    audio_precalcs();
    setpal();

    engine.mixBufSize = (1024 + SET_MAXROWLGT) * sizeof(Sint32);
    engine.mixBuf = malloc(engine.mixBufSize);

    // Start audio, enter main loop
    dmPrint("We are go.\n");
    SDL_LockAudio();
    SDL_PauseAudio(0);
    SDL_UnlockAudio();
    engine.startTime = SDL_GetTicks();

    while (!engine.exitFlag)
    {
        // Handle SDL events
        while (SDL_PollEvent(&engine.event))
        switch (engine.event.type)
        {
            case SDL_KEYDOWN:
                switch (engine.event.key.keysym.sym)
                {
                    case SDLK_ESCAPE:
                        engine.exitFlag = TRUE;
                        break;
                    
                    case SDLK_f:
                        engine.optVFlags ^= SDL_FULLSCREEN;
                        if (!engineInitializeVideo())
                            goto error_exit;
                        break;

                    case SDLK_RETURN:
                        if (engine.event.key.keysym.mod & KMOD_ALT)
                        {
                            engine.optVFlags ^= SDL_FULLSCREEN;
                            if (!engineInitializeVideo())
                                goto error_exit;
                        }
                        break;

                    default:
                        break;
                }

                break;

            case SDL_VIDEOEXPOSE:
                break;

            case SDL_QUIT:
                engine.exitFlag = TRUE;
                break;
        }

        // Draw frame
        engine.frameTime = SDL_GetTicks();
        int qt = engineGetTick(&engine);

        if (SDL_MUSTLOCK(engine.screen) != 0 && SDL_LockSurface(engine.screen) != 0)
        {
            dmError("Can't lock surface.\n");
            goto error_exit;
        }

        // Main rendering code
        while ((qt / SET_ROWTIX >= *dez) && (flagz & DEMOEND) == 0)
        {
            dez++;
            flagz = *dez++;
            if (flagz & FLASHTXT)
                flixtim = *(dez - 2);

            if (flagz & TXTSCR)
                dizainword = dotxtscr();
        }

        if (flagz & FLASHTXT)
        {
            while ((qt / SET_ROWTIX) >= flixtim)
            {
                phiword = lyrix();
                flixtim += 4;
            }
        }

        if (flagz & DEMOEND)
            engine.exitFlag = TRUE;

        if (flagz & BLACKBG)
        {
            SDL_FillRect(engine.screen, NULL, 0);
        }
        else
        if (flagz & FLASHBG)
        {
            unsigned char col = 130 + (qt % 48) * 2;
            SDL_FillRect(engine.screen, NULL, col);
        }

        if (flagz & CHESSBG)
        {
            int zoom = (10 + abs(((qt >> 1) % 96) - 48)) * 4096 / SET_VID_BUFW;
            rotochess(engine.screen, sin(qt * 0.03) * zoom, cos(qt * 0.03) * zoom, 0, 0);
        }

        //if(flagz&PLASMABG) drawplasma(ruutu, qt);

        if (flagz & OCSALOGO)
        {
            txtDrawChar(U * 6, U * 4, phont['O' - 32], U + U * sin(qt * 0.10 + 3),
                     U);
            txtDrawChar(U * 14, U * 4, phont['C' - 32], U,
                     U + U * sin(qt * 0.11 + 3));
            txtDrawChar(U * 22, U * 4, phont['S' - 32], U,
                     U + U * sin(qt * 0.12 + 3));
            txtDrawChar(U * 30, U * 4, phont['A' - 32],
                     U + U * sin(qt * 0.13 + 3), U);
        }

        if (flagz & SCROLL0)
            plainscroll(qt);

        if (flagz & BALLIE)
        {
            int zoom;
            vec3d joo;
            if (flagz & BALLJUMPS)
                zoom = abs((qt % 96) - 48);
            else
                zoom = 47;
            
            zoom = dmClamp(zoom, 0, 47);

            unitvec(&joo, 0.038 * qt, 0.023 * qt, 0.011 * qt,
                    32000 / balltab[zoom].R);
            joo.z <<= 1;

            drawball(engine.screen, &joo, zoom);
        }

        if (flagz & FLASHTXT)
            flashtxt(phiword);

        if ((flagz & TXTSCR) && ((qt / SET_ROWTIX) & 2))
            drawtxtscr(dizainword);

        if (flagz & ENDSCR)
            doendscroll(qt - 1024 * SET_ROWTIX);

        if (flagz & COUNTAH)
        {
            int n = ((qt * 50 / 48) - 256 * 6);
            int dis = (rand() % U) >> 1;
            if (n > 666)
                n = 666;

            if (n > 600)
            {
                txtDrawChar(U * 12 + dis, (SET_VID_BUFH >> 1) + dis + U * 6,
                         phont['X' - 32], U, U);
                txtDrawChar(U * 22 + dis, (SET_VID_BUFH >> 1) + dis + U * 6,
                         phont['3' - 32], U, U);
            }

            txtDrawChar(U * 28 + dis, SET_VID_BUFH >> 1, phont[16 + (n % 10)], U, U);
            n /= 10;
            txtDrawChar(U * 18 + dis, SET_VID_BUFH >> 1, phont[16 + (n % 10)], U, U);
            n /= 10;
            txtDrawChar(U * 8 + dis, SET_VID_BUFH >> 1, phont[16 + (n % 10)], U, U);
            n /= 10;
        }

        // Flip screen, increase frame count, wait
        if (SDL_MUSTLOCK(engine.screen) != 0)
            SDL_UnlockSurface(engine.screen);

        engine.frameCount++;
        SDL_Flip(engine.screen);
        SDL_Delay(20);
    }

error_exit:
    // Shutdown
    dmPrint("Shutting down.\n");
    SDL_ShowCursor(SDL_ENABLE);
    if (engine.screen)
        SDL_FreeSurface(engine.screen);

    SDL_LockAudio();
    SDL_PauseAudio(1);
    SDL_UnlockAudio();

    audio_close();

    if (initSDL)
        SDL_Quit();

    if (engine.mixBuf)
        free(engine.mixBuf);

    return 0;
}