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.

Build information to get you started:

The first half of this file explains the .ART, .MAP, and PALETTE.DAT formats.
The second half has documentation about every BUILD engine function, what it
   does, and what the parameters are.

-Ken S.

------------------------------------------------------------------------------
Documentation on Ken's .ART file format                     by Ken Silverman

   I am documenting my ART format to allow you to program your own custom
art utilites if you so desire.  I am still planning on writing the script
system.

   All art files must have xxxxx###.ART.  When loading an art file you
should keep trying to open new xxxxx###'s, incrementing the number, until
an art file is not found.

1. long artversion;

      The first 4 bytes in the art format are the version number.  The current
   current art version is now 1.  If artversion is not 1 then either it's the
   wrong art version or something is wrong.

2. long numtiles;

      Numtiles is not really used anymore.  I wouldn't trust it.  Actually
   when I originally planning art version 1 many months ago, I thought I
   would need this variable, but it turned it is was unnecessary.  To get
   the number of tiles, you should search all art files, and check the
   localtilestart and localtileend values for each file.

3. long localtilestart;

      Localtilestart is the tile number of the first tile in this art file.

4. long localtileend;

      Localtileend is the tile number of the last tile in this art file.
      Note:  Localtileend CAN be higher than the last used slot in an art
      file.

         Example:  If you chose 256 tiles per art file:
      TILES000.ART -> localtilestart = 0,   localtileend = 255
      TILES001.ART -> localtilestart = 256, localtileend = 511
      TILES002.ART -> localtilestart = 512, localtileend = 767
      TILES003.ART -> localtilestart = 768, localtileend = 1023

5. short tilesizx[localtileend-localtilestart+1];

      This is an array of shorts of all the x dimensions of the tiles
   in this art file.  If you chose 256 tiles per art file then
   [localtileend-localtilestart+1] should equal 256.

6. short tilesizy[localtileend-localtilestart+1];

      This is an array of shorts of all the y dimensions.

7. long picanm[localtileend-localtilestart+1];

      This array of longs stores a few attributes for each tile that you
   can set inside EDITART.  You probably won't be touching this array, but
   I'll document it anyway.

   Bit:  |31           24|23           16|15            8|7             0|
         -----------------------------------------------------------------
         | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
         -----------------------------------------------------------------
                 | Anim. |  Signed char  |  Signed char  |   |  Animate  |
                 | Speed |   Y-center    |   X-center    |   |   number  |
                 --------|    offset     |    offset     |   |------------
                         --------------------------------|   ------------
                                                         | Animate type:|
                                                         | 00 - NoAnm   |
                                                         | 01 - Oscil   |
                                                         | 10 - AnmFd   |
                                                         | 11 - AnmBk   |
                                                         ----------------
          You probably recognize these:
       Animate speed -            EDITART key: 'A', + and - to adjust
       Signed char x&y offset -   EDITART key: '`', Arrows to adjust
       Animate number&type -      EDITART key: +/- on keypad

8. After the picanm's, the rest of the file is straight-forward rectangular
      art data.  You must go through the tilesizx and tilesizy arrays to find
      where the artwork is actually stored in this file.

      Note:  The tiles are stored in the opposite coordinate system than
         the screen memory is stored.  Example on a 4*4 file:

         Offsets:
         -----------------
         | 0 | 4 | 8 |12 |
         -----------------
         | 1 | 5 | 9 |13 |
         -----------------
         | 2 | 6 |10 |14 |
         -----------------
         | 3 | 7 |11 |15 |
         -----------------

----------------------------------------------------------------------------
   If you wish to display the artwork, you will also need to load your
palette.  To load the palette, simply read the first 768 bytes of your
palette.dat and write it directly to the video card - like this:

   Example:
      long i, fil;

      fil = open("palette.dat",O_BINARY|O_RDWR,S_IREAD);
      read(fil,&palette[0],768);
      close(fil);

      outp(0x3c8,0);
      for(i=0;i<768;i++)
         outp(0x3c9,palette[i]);
------------------------------------------------------------------------------

Packet format for DUKE3D (specifically for network mode 1, n(n-1) mode):

Example bunch of packets:
A  B  C   D  E  F     G      H      I   J  K  L  M   N...       O
---------------------------------------------------------------------
d9 00 d9  11 01 00  -  -   -  -   -  -   -  -  -  -  4f       16 31
da 00 da  11 01 00  -  -   -  -   -  -   -  -  -  -  b2       b7 9d
db 00 db  11 01 00  -  -   -  -   -  -   -  -  -  -  b1       24 62
dc 00 dc  11 01 00  -  -   -  -   -  -   -  -  -  -  ca       1d 58
dd 00 dd  11 01 00  -  -   -  -   -  -   -  -  -  -  a9       94 14
de 00 de  11 01 05  00 00  -  -   03 00  -  -  -  -  c5       50 b9
df 00 df  11 01 0f  a1 ff  fe 09  00 00  26 -  -  -  e2       88 6f
e0 00 e0  11 01 04  -  -   -  -   fd ff  -  -  -  -  77       51 d7
e1 00 e1  11 01 03  1f 00  ff 09  -  -   -  -  -  -  ac       14 b7
e2 00 e2  11 01 0b  9c 00  fb 09  -  -   24 -  -  -  f8       6c 22

GAME sends fields D-N
MMULTI adds fields A-C and O for error correction.

A: Packet count sending modulo 256
B: Error state.  Usually 0.  To request a resend, bit 0 is set.  In order
      to catch up on networks, sending many packets is bad, so 2 packets
      are sent in 1 IPX packet.  To send 2 packets in 1 packet, bit 1 is set.
      In special cases, this value may be different.
C: Packet count receiving modulo 256

D: Message header byte.  These are all the possible values currently.  You
      are probably only interested in case 17.  Note that fields E-N apply
      to case 17 only.
     0: send movement info from master to slave (network mode 0 only)
     1: send movement info from slave to master (network mode 0 only)
     4: user-typed messages
     5: Re-start level with given parameters
     6: Send player name
     7: Play Remote Ridicule sound
     8: Re-start level with given parameters for a user map
    17: send movement info to everybody else (network mode 1 only)
   250: Wait for Everybody (Don't start until everybody's done loading)
   255: Player quit to DOS

E: Timing byte used to calculate lag time.  This prevents the 2 computer's
      timers from drifting apart.

F: Bits field byte.  Fields G-M are sent only when certain bits
      in this byte are set.

G: X momentum update (2 bytes).  Sent only if ((F&1) != 0)

H: Y momentum update (2 bytes).  Sent only if ((F&2) != 0)

I: Angle momentum update (2 bytes).  Sent only if ((F&4) != 0)

J: The states of 8 different keys (1 byte).  Sent only if ((F&8) != 0)
K: The states of 8 different keys (1 byte).  Sent only if ((F&16) != 0)
L: The states of 8 different keys (1 byte).  Sent only if ((F&32) != 0)
M: The states of 8 different keys (1 byte).  Sent only if ((F&64) != 0)

N: Sync checking byte.  Useful for debugging programming errors.  Can be a
   variable number of bytes.  Actual number of sync checking bytes is
   calculated by length of the whole packet minus the rest of the bytes sent.

O: CRC-16

------------------------------------------------------------------------------
|   @@@@@@@@@@@     @@@       @@@  @@@@@@@@@  @@@           @@@@@@@@@        |
|   @@@@@@@@@@@@@   @@@       @@@  @@@@@@@@@  @@@           @@@@@@@@@@@      |
|   @@@       @@@@  @@@       @@@     @@@     @@@           @@@    @@@@@     |
|   @@@        @@@  @@@       @@@     @@@     @@@           @@@      @@@@    |
|   @@@       @@@@  @@@       @@@     @@@     @@@           @@@       @@@    |
|   @@@@@@@@@@@@@   @@@       @@@     @@@     @@@           @@@       @@@    |
|   @@@@@@@@@@@@@   @@@       @@@     @@@     @@@           @@@       @@@    |
|   @@@       @@@@  @@@       @@@     @@@     @@@           @@@       @@@    |
|   @@@        @@@  @@@       @@@     @@@     @@@           @@@      @@@@    |
|   @@@       @@@@  @@@@     @@@@     @@@     @@@           @@@    @@@@@     |
|   @@@@@@@@@@@@@    @@@@@@@@@@@   @@@@@@@@@  @@@@@@@@@@@@  @@@@@@@@@@@      |
|   @@@@@@@@@@@        @@@@@@@     @@@@@@@@@  @@@@@@@@@@@@  @@@@@@@@@        |
|                                                                            |
|                          M A P   F O R M A T !                             |
------------------------------------------------------------------------------

Here is Ken's documentation on the COMPLETE BUILD map format:
BUILD engine and editor programmed completely by Ken Silverman

Here's how you should read a BUILD map file:
{
   fil = open(???);

      //Load map version number (current version is 7L)
   read(fil,&mapversion,4);

      //Load starting position
   read(fil,posx,4);
   read(fil,posy,4);
   read(fil,posz,4);          //Note: Z coordinates are all shifted up 4
   read(fil,ang,2);           //All angles are from 0-2047, clockwise
   read(fil,cursectnum,2);    //Sector of starting point

      //Load all sectors (see sector structure described below)
   read(fil,&numsectors,2);
   read(fil,&sector[0],sizeof(sectortype)*numsectors);

      //Load all walls (see wall structure described below)
   read(fil,&numwalls,2);
   read(fil,&wall[0],sizeof(walltype)*numwalls);

      //Load all sprites (see sprite structure described below)
   read(fil,&numsprites,2);
   read(fil,&sprite[0],sizeof(spritetype)*numsprites);

   close(fil);
}

      -------------------------------------------------------------
      | @@@@@@@ @@@@@@@ @@@@@@@ @@@@@@@@ @@@@@@@ @@@@@@@  @@@@@@@ |
      | @@      @@      @@         @@    @@   @@ @@   @@@ @@      |
      | @@@@@@@ @@@@@   @@         @@    @@   @@ @@@@@@@  @@@@@@@ |
      |      @@ @@      @@         @@    @@   @@ @@   @@@      @@ |
      | @@@@@@@ @@@@@@@ @@@@@@@    @@    @@@@@@@ @@    @@ @@@@@@@ |
      -------------------------------------------------------------

   //sizeof(sectortype) = 40
typedef struct
{
   short wallptr, wallnum;
   long ceilingz, floorz;
   short ceilingstat, floorstat;
   short ceilingpicnum, ceilingheinum;
   signed char ceilingshade;
   char ceilingpal, ceilingxpanning, ceilingypanning;
   short floorpicnum, floorheinum;
   signed char floorshade;
   char floorpal, floorxpanning, floorypanning;
   char visibility, filler;
   short lotag, hitag, extra;
} sectortype;
sectortype sector[1024];

wallptr - index to first wall of sector
wallnum - number of walls in sector
z's - z coordinate (height) of ceiling / floor at first point of sector
stat's
   bit 0: 1 = parallaxing, 0 = not                                 "P"
   bit 1: 1 = sloped, 0 = not
   bit 2: 1 = swap x&y, 0 = not                                    "F"
   bit 3: 1 = double smooshiness                                   "E"
   bit 4: 1 = x-flip                                               "F"
   bit 5: 1 = y-flip                                               "F"
   bit 6: 1 = Align texture to first wall of sector                "R"
   bits 7-15: reserved
picnum's - texture index into art file
heinum's - slope value (rise/run) (0-parallel to floor, 4096-45 degrees)
shade's - shade offset of ceiling/floor
pal's - palette lookup table number (0 - use standard colors)
panning's - used to align textures or to do texture panning
visibility - determines how fast an area changes shade relative to distance
filler - useless byte to make structure aligned
lotag, hitag, extra - These variables used by the game programmer only

          -----------------------------------------------
          | @@      @@ @@@@@@@@ @@      @@      @@@@@@@ |
          | @@      @@ @@    @@ @@      @@      @@      |
          | @@  @@  @@ @@@@@@@@ @@      @@      @@@@@@@ |
          | @@ @@@@ @@ @@    @@ @@      @@           @@ |
          |  @@@ @@@@  @@    @@ @@@@@@@ @@@@@@@ @@@@@@@ |
          ----------------------------------------------|

   //sizeof(walltype) = 32
typedef struct
{
   long x, y;
   short point2, nextwall, nextsector, cstat;
   short picnum, overpicnum;
   signed char shade;
   char pal, xrepeat, yrepeat, xpanning, ypanning;
   short lotag, hitag, extra;
} walltype;
walltype wall[8192];

x, y: Coordinate of left side of wall, get right side from next wall's left side
point2: Index to next wall on the right (always in the same sector)
nextwall: Index to wall on other side of wall (-1 if there is no sector)
nextsector: Index to sector on other side of wall (-1 if there is no sector)
cstat:
   bit 0: 1 = Blocking wall (use with clipmove, getzrange)         "B"
   bit 1: 1 = bottoms of invisible walls swapped, 0 = not          "2"
   bit 2: 1 = align picture on bottom (for doors), 0 = top         "O"
   bit 3: 1 = x-flipped, 0 = normal                                "F"
   bit 4: 1 = masking wall, 0 = not                                "M"
   bit 5: 1 = 1-way wall, 0 = not                                  "1"
   bit 6: 1 = Blocking wall (use with hitscan / cliptype 1)        "H"
   bit 7: 1 = Transluscence, 0 = not                               "T"
   bit 8: 1 = y-flipped, 0 = normal                                "F"
   bit 9: 1 = Transluscence reversing, 0 = normal                  "T"
   bits 10-15: reserved
picnum - texture index into art file
overpicnum - texture index into art file for masked walls / 1-way walls
shade - shade offset of wall
pal - palette lookup table number (0 - use standard colors)
repeat's - used to change the size of pixels (stretch textures)
pannings - used to align textures or to do texture panning
lotag, hitag, extra - These variables used by the game programmer only

      -------------------------------------------------------------
      | @@@@@@@ @@@@@@@ @@@@@@@   @@@@@@ @@@@@@@@ @@@@@@@ @@@@@@@ |
      | @@      @@   @@ @@   @@@    @@      @@    @@      @@      |
      | @@@@@@@ @@@@@@@ @@@@@@@     @@      @@    @@@@@   @@@@@@@ |
      |      @@ @@      @@    @@    @@      @@    @@           @@ |
      | @@@@@@@ @@      @@    @@  @@@@@@    @@    @@@@@@@ @@@@@@@ |
      -------------------------------------------------------------

   //sizeof(spritetype) = 44
typedef struct
{
   long x, y, z;
   short cstat, picnum;
   signed char shade;
   char pal, clipdist, filler;
   unsigned char xrepeat, yrepeat;
   signed char xoffset, yoffset;
   short sectnum, statnum;
   short ang, owner, xvel, yvel, zvel;
   short lotag, hitag, extra;
} spritetype;
spritetype sprite[4096];
x, y, z - position of sprite - can be defined at center bottom or center
cstat:
   bit 0: 1 = Blocking sprite (use with clipmove, getzrange)       "B"
   bit 1: 1 = transluscence, 0 = normal                            "T"
   bit 2: 1 = x-flipped, 0 = normal                                "F"
   bit 3: 1 = y-flipped, 0 = normal                                "F"
   bits 5-4: 00 = FACE sprite (default)                            "R"
             01 = WALL sprite (like masked walls)
             10 = FLOOR sprite (parallel to ceilings&floors)
   bit 6: 1 = 1-sided sprite, 0 = normal                           "1"
   bit 7: 1 = Real centered centering, 0 = foot center             "C"
   bit 8: 1 = Blocking sprite (use with hitscan / cliptype 1)      "H"
   bit 9: 1 = Transluscence reversing, 0 = normal                  "T"
   bits 10-14: reserved
   bit 15: 1 = Invisible sprite, 0 = not invisible
picnum - texture index into art file
shade - shade offset of sprite
pal - palette lookup table number (0 - use standard colors)
clipdist - the size of the movement clipping square (face sprites only)
filler - useless byte to make structure aligned
repeat's - used to change the size of pixels (stretch textures)
offset's - used to center the animation of sprites
sectnum - current sector of sprite
statnum - current status of sprite (inactive/monster/bullet, etc.)

ang - angle the sprite is facing
owner, xvel, yvel, zvel, lotag, hitag, extra - These variables used by the
                                               game programmer only
------------------------------------------------------------------------------

-----------------------------------------------------------------------------
|                        IMPORTANT ENGINE FUNCTIONS:                        |
-----------------------------------------------------------------------------

initengine()
   Initializes many variables for the BUILD engine.  You should call this
   once before any other functions of the BUILD engine are used.

uninitengine();
   Frees buffers.  You should call this once at the end of the program
   before quitting to dos.

loadboard(char *filename, long *posx, long *posy, long *posz, short *ang, short *cursectnum)
saveboard(char *filename, long *posx, long *posy, long *posz, short *ang, short *cursectnum)
   Loads/saves the given board file from memory.  Returns -1 if file not
   found.  If no extension is given, .MAP will be appended to the filename.

loadpics(char *filename);
   Loads the given artwork file into memory for the BUILD engine.
   Returns -1 if file not found.  If no extension is given, .ART will
   be appended to the filename.

loadtile(short tilenum)
   Loads a given tile number from disk into memory if it is not already in
   memory.  This function calls allocache internally.  A tile is not in the
   cache if (waloff[tilenum] == 0)

-----------------------------------------------------------------------------
|                          SCREEN STATUS FUNCTIONS:                         |
-----------------------------------------------------------------------------

setgamemode(char vidoption, long xdim, long ydim);
   This function sets the video mode to 320*200*256color graphics.
   Since BUILD supports several different modes including mode x,
   mode 13h, and other special modes, I don't expect you to write
   any graphics output functions.  (Soon I have all the necessary
   functions)  If for some reason, you use your own graphics mode,
   you must call this function again before using the BUILD drawing
   functions.

   vidoption can be anywhere from 0-6
   xdim,ydim can be any vesa resolution if vidoption = 1
   xdim,ydim must be 320*200 for any other mode.
      (see graphics mode selection in my setup program)

setview(long x1, long y1, long x2, long y2)
   Sets the viewing window to a given rectangle of the screen.
   Example: For full screen 320*200, call like this: setview(0L,0L,319L,199L);

nextpage();
   This function flips to the next video page.  After a screen is prepared,
   use this function to view the screen.

-----------------------------------------------------------------------------
|                             DRAWING FUNCTIONS:                            |
-----------------------------------------------------------------------------

drawrooms(long posx, long posy, long posz, short ang, long horiz, short cursectnum)
   This function draws the 3D screen to the current drawing page,
   which is not yet shown.  This way, you can overwrite some things
   over the 3D screen such as a gun.  Be sure to call the drawmasks()
   function soon after you call the drawrooms() function.  To view
   the screen, use the nextpage() function.  The nextpage() function
   should always be called sometime after each draw3dscreen()
   function.

drawmasks();
   This function draws all the sprites and masked walls to the current
   drawing page which is not yet shown.  The reason I have the drawing
   split up into these 2 routines is so you can animate just the
   sprites that are about to be drawn instead of having to animate
   all the sprites on the whole board.  Drawrooms() prepares these
   variables:  spritex[], spritey[], spritepicnum[], thesprite[],
   and spritesortcnt.  Spritesortcnt is the number of sprites about
   to be drawn to the page.  To change the sprite's picnum, simply
   modify the spritepicnum array  If you want to change other parts
   of the sprite structure, then you can use the thesprite array to
   get an index to the actual sprite number.

clearview(long col)
   Clears the current video page to the given color

clearallviews(long col)
   Clears all video pages to the given color

drawmapview (long x, long y, long zoom, short ang)
   Draws the 2-D texturized map at the given position into the viewing window.

rotatesprite (long sx, long sy, long z, short a, short picnum,
              signed char dashade, char dapalnum, char dastat,
              long cx1, long cy1, long cx2, long cy2)
   (sx, sy) is the center of the sprite to draw defined as
       screen coordinates shifted up by 16.
   (z) is the zoom.  Normal zoom is 65536.
       Ex: 131072 is zoomed in 2X and 32768 is zoomed out 2X.
   (a) is the angle (0 is straight up)
   (picnum) is the tile number
   (dashade) is 0 normally but can be any standard shade up to 31 or 63.
   (dapalnum) can be from 0-255.
   if ((dastat&1) == 0) - no transluscence
   if ((dastat&1) != 0) - transluscence
   if ((dastat&2) == 0) - don't scale to setview's viewing window
   if ((dastat&2) != 0) - scale to setview's viewing window (windowx1,etc.)
   if ((dastat&4) == 0) - nuttin' special
   if ((dastat&4) != 0) - y-flip image
   if ((dastat&8) == 0) - clip to startumost/startdmost
   if ((dastat&8) != 0) - don't clip to startumost/startdmost
   if ((dastat&16) == 0) - use Editart center as point passed
   if ((dastat&16) != 0) - force point passed to be top-left corner
   if ((dastat&32) == 0) - nuttin' special
   if ((dastat&32) != 0) - use reverse transluscence
   if ((dastat&64) == 0) - masked drawing (check 255's) (slower)
   if ((dastat&64) != 0) - draw everything (don't check 255's) (faster)
   if ((dastat&128) == 0) - nuttin' special
   if ((dastat&128) != 0) - automatically draw to all video pages

   Note:  As a special case, if both ((dastat&2) != 0) and ((dastat&8) != 0)
      then rotatesprite will scale to the full screen (0,0,xdim-1,ydim-1)
      rather than setview's viewing window. (windowx1,windowy1,etc.)  This
      case is useful for status bars, etc.

      Ex: rotatesprite(160L<<16,100L<<16,65536,totalclock<<4,
                       DEMOSIGN,2,50L,50L,270L,150L);
          This example will draw the DEMOSIGN tile in the center of the
          screen and rotate about once per second.  The sprite will only
          get drawn inside the rectangle from (50,50) to (270,150)

drawline256(long x1, long y1, long x2, long y2, char col)
   Draws a solid line from (x1,y1) to (x2,y2) with color (col)
   For this function, screen coordinates are all shifted up 16 for precision.

printext256(long xpos, long ypos, short col, short backcol,
            char *message, char fontsize)
   Draws a text message to the screen.
   (xpos,ypos) - position of top left corner
   col - color of text
   backcol - background color, if -1, then background is transparent
   message - text message
   fontsize - 0 - 8*8 font
              1 - 4*6 font

-----------------------------------------------------------------------------
|                         MOVEMENT COLLISION FUNCTIONS:                     |
-----------------------------------------------------------------------------

clipmove(long *x, long *y, long *z, short *sectnum, long xvect, long yvect,
         long walldist, long ceildist, long flordist, unsigned long cliptype)
      Moves any object (x, y, z) in any direction at any velocity and will
   make sure the object will stay a certain distance from walls (walldist)
       Pass the pointers of the starting position (x, y, z).  Then
   pass the starting position's sector number as a pointer also.
   Also these values will be modified accordingly.  Pass the
   direction and velocity by using a vector (xvect, yvect).
   If you don't fully understand these equations, please call me.
         xvect = velocity * cos(angle)
         yvect = velocity * sin(angle)
      Walldist tells how close the object can get to a wall.  I use
    128L as my default.  If you increase walldist all of a sudden
    for a certain object, the object might leak through a wall, so
    don't do that!
       Cliptype is a mask that tells whether the object should be clipped
    to or not.  The lower 16 bits are anded with wall[].cstat and the higher
    16 bits are anded with sprite[].cstat.

     Clipmove can either return 0 (touched nothing)
                                32768+wallnum (wall first touched)
                                49152+spritenum (sprite first touched)

pushmove (long *x, long *y, long *z, short *sectnum,
          long walldist, long ceildist, long flordist, unsigned long cliptype)
      This function makes sure a player or monster (defined by x, y, z, sectnum)
   is not too close to a wall.  If it is, then it attempts to push it away.
   If after 256 tries, it is unable to push it away, it returns -1, in which
   case the thing should gib.

getzrange(long x, long y, long z, short sectnum,
                  long *ceilz, long *ceilhit,
                  long *florz, long *florhit,
                  long walldist, unsigned long cliptype)

      Use this in conjunction with clipmove.  This function will keep the
   player from falling off cliffs when you're too close to the edge.  This
   function finds the highest and lowest z coordinates that your clipping
   BOX can get to.  It must search for all sectors (and sprites) that go
   into your clipping box.  This method is better than using
   sector[cursectnum].ceilingz and sector[cursectnum].floorz because this
   searches the whole clipping box for objects, not just 1 point.
      Pass x, y, z, sector normally.  Walldist can be 128.  Cliptype is
   defined the same way as it is for clipmove.  This function returns the
   z extents in ceilz and florz. It will return the object hit in ceilhit
   and florhit.  Ceilhit and florhit will also be either:
                                 16384+sector (sector first touched) or
                                 49152+spritenum (sprite first touched)

hitscan(long xstart, long ystart, long zstart, short startsectnum,
        long vectorx, long vectory, long vectorz,
        short *hitsect, short *hitwall, short *hitsprite,
        long *hitx, long *hity, long *hitz);

   Pass the starting 3D position:
        (xstart, ystart, zstart, startsectnum)
   Then pass the 3D angle to shoot (defined as a 3D vector):
        (vectorx, vectory, vectorz)
   Then set up the return values for the object hit:
        (hitsect, hitwall, hitsprite)
   and the exact 3D point where the ray hits:
        (hitx, hity, hitz)

   How to determine what was hit:
      * Hitsect is always equal to the sector that was hit (always >= 0).

      * If the ray hits a sprite then:
           hitsect = thesectornumber
           hitsprite = thespritenumber
           hitwall = -1

       * If the ray hits a wall then:
           hitsect = thesectornumber
           hitsprite = -1
           hitwall = thewallnumber

       * If the ray hits the ceiling of a sector then:
           hitsect = thesectornumber
           hitsprite = -1
           hitwall = -1
           vectorz < 0
           (If vectorz < 0 then you're shooting upward which means
              that you couldn't have hit a floor)

       * If the ray hits the floor of a sector then:
           hitsect = thesectornumber
           hitsprite = -1
           hitwall = -1
           vectorz > 0
           (If vectorz > 0 then you're shooting downard which means
              that you couldn't have hit a ceiling)

neartag(long x, long y, long z, short sectnum, short ang,  //Starting position & angle
        short *neartagsector,   //Returns near sector if sector[].tag != 0
        short *neartagwall,     //Returns near wall if wall[].tag != 0
        short *neartagsprite,   //Returns near sprite if sprite[].tag != 0
        long *neartaghitdist,   //Returns actual distance to object (scale: 1024=largest grid size)
        long neartagrange,      //Choose maximum distance to scan (scale: 1024=largest grid size)
        char tagsearch)         //1-lotag only, 2-hitag only, 3-lotag&hitag
      Neartag works sort of like hitscan, but is optimized to
   scan only close objects and scan only objects with
   tags != 0.  Neartag is perfect for the first line of your space bar code.
   It will tell you what door you want to open or what switch you want to
   flip.

cansee(long x1, long y1, long z1, short sectnum1,
       long x2, long y2, long z2, short sectnum2);  returns 0 or 1
   This function determines whether or not two 3D points can "see" each
   other or not.  All you do is pass it the coordinates of a 3D line defined
   by two 3D points (with their respective sectors)  The function will return
   a 1 if the points can see each other or a 0 if there is something blocking
   the two points from seeing each other.  This is how I determine whether a
   monster can see you or not. Try playing DOOM1.DAT to fully enjoy this
   great function!

updatesector(long x, long y, &sectnum);
   This function updates the sector number according to the x and y values
   passed to it.  Be careful when you use this function with sprites because
   remember that the sprite's sector number should not be modified directly.
   If you want to update a sprite's sector, I recomment using the setsprite
   function described below.

inside(long x, long y, short sectnum);
   Tests to see whether the overhead point (x, y) is inside sector (sectnum)
   Returns either 0 or 1, where 1 means it is inside, and 0 means it is not.

clipinsidebox(long x, long y, short wallnum, long walldist)
   Returns TRUE only if the given line (wallnum) intersects the square with
   center (x,y) and radius, walldist.

dragpoint(short wallnum, long newx, long newy);
   This function will drag a point in the exact same way a point is dragged
   in 2D EDIT MODE using the left mouse button.  Simply pass it which wall
   to drag and then pass the new x and y coordinates for that point.
   Please use this function because if you don't and try to drag points
   yourself, I can guarantee that it won't work as well as mine and you
   will get confused.  Note:  Every wall of course has 2 points.  When you
   pass a wall number to this function, you are actually passing 1 point,
   the left side of the wall (given that you are in the sector of that wall)
   Got it?

-----------------------------------------------------------------------------
|                           MATH HELPER FUNCTIONS:                          |
-----------------------------------------------------------------------------

krand()
   Random number function - returns numbers from 0-65535

ksqrt(long num)
   Returns the integer square root of the number.

getangle(long xvect, long yvect)
   Gets the angle of a vector (xvect,yvect)
   These are 2048 possible angles starting from the right, going clockwise

rotatepoint(long xpivot, long ypivot, long x, long y,
            short daang, long *x2, long *y2);
      This function is a very convenient and fast math helper function.
   Rotate points easily with this function without having to juggle your
   cosines and sines.  Simply pass it:

       Input:   1. Pivot point     (xpivot,ypivot)
                2. Original point  (x,y)
                3. Angle to rotate (0 = nothing, 512 = 90� CW, etc.)
       Output:  4. Rotated point   (*x2,*y2)

lastwall(short point);
      Use this function as a reverse function of wall[].point2.  In order
   to save memory, my walls are only on a single linked list.

nextsectorneighborz(short sectnum, long thez, short topbottom, short direction)
   This function is used to tell where elevators should stop.  It searches
   nearby sectors for the next closest ceilingz or floorz it should stop at.
   sectnum - elevator sector
   thez - current z to start search from
   topbottom - search ceilingz's/floorz's only
   direction - search upwards/downwards

getceilzofslope(short sectnum, long x, long y)
getflorzofslope(short sectnum, long x, long y)
getzsofslope(short sectnum, long x, long y, long *ceilz, long *florz)
   These 3 functions get the height of a ceiling and/or floor in a sector
   at any (x,y) location.  Use getzsofslope only if you need both the ceiling
   and floor.

alignceilslope(short sectnum, long x, long y, long z)
alignflorslope(short sectnum, long x, long y, long z)
   Given a sector and assuming it's first wall is the pivot wall of the slope,
   this function makes the slope pass through the x,y,z point.  One use of
   this function is used for sin-wave floors.

-----------------------------------------------------------------------------
|                             SPRITE FUNCTIONS:                             |
-----------------------------------------------------------------------------

insertsprite(short sectnum, short statnum);   //returns (short)spritenum;
   Whenever you insert a sprite, you must pass it the sector
   number, and a status number (statnum).  The status number can be any
   number from 0 to MAXSTATUS-1.  Insertsprite works like a memory
   allocation function and returns the sprite number.

deletesprite(short spritenum);
   Deletes the sprite.

changespritesect(short spritenum, short newsectnum);
   Changes the sector of sprite (spritenum) to the
   newsector (newsectnum).  This function may become
   internal to the engine in the movesprite function.  But
   this function is necessary since all the sectors have
   their own doubly-linked lists of sprites.

changespritestat(short spritenum, short newstatnum);
   Changes the status of sprite (spritenum) to status
   (newstatus).  Newstatus can be any number from 0 to MAXSTATUS-1.
   You can use this function to put a monster on a list of active sprites
   when it first sees you.

setsprite(short spritenum, long newx, long newy, long newz);
      This function simply sets the sprite's position to a specified
   coordinate (newx, newy, newz) without any checking to see
   whether the position is valid or not.  You could directly
   modify the sprite[].x, sprite[].y, and sprite[].z values, but
   if you use my function, the sprite is guaranteed to be in the
   right sector.

-----------------------------------------------------------------------------
|                             CACHE FUNCTIONS:                              |
-----------------------------------------------------------------------------
initcache(long dacachestart, long dacachesize)
   First allocate a really large buffer (as large as possible), then pass off
   the memory bufer the initcache
   dacachestart: 32-bit offset in memory of start of cache
   dacachesize: number of bytes that were allocated for the cache to use

allocache (long *bufptr, long bufsiz, char *lockptr)
   *bufptr = pointer to 4-byte pointer to buffer.  This
      allows allocache to remove previously allocated things
      from the cache safely by setting the 4-byte pointer to 0.
   bufsiz = number of bytes to allocate
   *lockptr = pointer to locking char which tells whether
      the region can be removed or not.  If *lockptr = 0 then
      the region is not locked else its locked.

-----------------------------------------------------------------------------
|                          GROUP FILE FUNCTIONS:                            |
-----------------------------------------------------------------------------
initgroupfile(char *filename)
   Tells the engine what the group file name is.
   You should call this before any of the following group file functions.
uninitgroupfile()
   Frees buffers.  You should call this once at the end of the program
   before quitting to dos.

kopen4load(char *filename, char searchfirst)
   Open a file.  First tries to open a stand alone file.  Then searches for
   it in the group file.  If searchfirst is nonzero, it will check the group
   file only.

kread(long handle, void *buffer, long leng)
klseek(long handle, long offset, long whence)
kfilelength(long handle)
kclose(long handle)
   These 4 functions simply shadow the dos file functions - they
   can do file I/O on the group file in addition to stand-along files.

-----------------------------------------------------------------------------
|                          COMMUNICATIONS FUNCTIONS:                        |
-----------------------------------------------------------------------------
   Much of the following code is to keep compatibity with older network code:

initmultiplayers(char damultioption, char dacomrateoption, char dapriority)
   The parameters are ignored - just pass 3 0's
uninitmultiplayers()    Does nothing

sendpacket(long other, char *bufptr, long messleng)
   other - who to send the packet to
   bufptr - pointer to message to send
   messleng - length of message
short getpacket (short *other, char *bufptr)
   returns the number of bytes of the packet received, 0 if no packet
   other - who the packet was received from
   bufptr - pointer to message that was received

sendlogon()             Does nothing
sendlogoff()
   Sends a packet to everyone else where the
   first byte is 255, and the
   second byte is myconnectindex

getoutputcirclesize()  Does nothing - just a stub function, returns 0
setsocket(short newsocket) Does nothing

flushpackets()
   Clears all packet buffers
genericmultifunction(long other, char *bufptr, long messleng, long command)
   Passes a buffer to the commit driver.  This command provides a gateway
   for game programmer to access COMMIT directly.

-----------------------------------------------------------------------------
|                             PALETTE FUNCTIONS:                            |
-----------------------------------------------------------------------------
VBE_setPalette(long start, long num, char *palettebuffer)
VBE_getPalette(long start, long num, char *palettebuffer)
   Set (num) palette palette entries starting at (start)
   palette entries are in a 4-byte format in this order:
      0: Blue (0-63)
      1: Green (0-63)
      2: Red (0-63)
      3: Reserved

makepalookup(long palnum, char *remapbuf,
             signed char r, signed char g, signed char b,
             char dastat)
   This function allows different shirt colors for sprites.  First prepare
   remapbuf, which is a 256 byte buffer of chars which the colors to remap.
   Palnum can be anywhere from 1-15.  Since 0 is where the normal palette is
   stored, it is a bad idea to call this function with palnum=0.
   In BUILD.H notice I added a new variable, spritepal[MAXSPRITES].
   Usually the value of this is 0 for the default palette.  But if you
   change it to the palnum in the code between drawrooms() and drawmasks
   then the sprite will be drawn with that remapped palette.  The last 3
   parameters are the color that the palette fades to as you get further
   away.  This color is normally black (0,0,0).  White would be (63,63,63).
   if ((dastat&1) == 0) then makepalookup will allocate & deallocate
   the memory block for use but will not waste the time creating a palookup
   table (assuming you will create one yourself)

setbrightness(char gammalevel, char *dapal)
   Use this function to adjust for gamma correction.
   Gammalevel - ranges from 0-15, 0 is darkest, 15 brightest. Default: 0
   dapal: standard VGA palette (768 bytes)

------------------------------------------------------------------------------
|                     This document brought to you by:                       |
|                                                                            |
|          @@@@@@        @@@@@@  @@@@@@@@@@@@@@@@@@  @@@@@@@@         @@@@@@ |
|          @@@@@@       @@@@@@   @@@@@@@@@@@@@@@@@@  @@@@@@@@@        @@@@@@ |
|          @@@@@@     @@@@@@@    @@@@@@@@@@@@@@@@@@  @@@@@@@@@@       @@@@@@ |
|          @@@@@@    @@@@@@@     @@@@@@              @@@@@@@@@@@      @@@@@@ |
|          @@@@@@  @@@@@@@       @@@@@@              @@@@@@@@@@@@     @@@@@@ |
|          @@@@@@ @@@@@@@        @@@@@@              @@@@@@@@@@@@@    @@@@@@ |
|          @@@@@@@@@@@@          @@@@@@@@@@@@@@@     @@@@@@ @@@@@@@   @@@@@@ |
| @@@@@@@  @@@@@@@@@@@           @@@@@@@@@@@@@@@     @@@@@@  @@@@@@@  @@@@@@ |
|          @@@@@@@@@@@@          @@@@@@@@@@@@@@@     @@@@@@   @@@@@@@ @@@@@@ |
|          @@@@@@ @@@@@@@        @@@@@@              @@@@@@    @@@@@@@@@@@@@ |
|          @@@@@@  @@@@@@@       @@@@@@              @@@@@@     @@@@@@@@@@@@ |
|          @@@@@@    @@@@@@@     @@@@@@              @@@@@@      @@@@@@@@@@@ |
|          @@@@@@     @@@@@@@    @@@@@@@@@@@@@@@@@@  @@@@@@       @@@@@@@@@@ |
|          @@@@@@       @@@@@@   @@@@@@@@@@@@@@@@@@  @@@@@@        @@@@@@@@@ |
|          @@@@@@        @@@@@@  @@@@@@@@@@@@@@@@@@  @@@@@@         @@@@@@@@ |
|                                                                            |
|                   Ken Silverman of East Greenwich, RI USA                  |
------------------------------------------------------------------------------


code:
KEY:
$ - Should be done soon!
> - Something to do eventually...
? - Is this really necessary?
B - Bug!

ENGINE -

  Drawing bugs:
    Make all VESA buffer-modes work with multiple drawrooms per nextpage
    Fix off-by-1 line on top of face sprites
    Fix selector initialization bug in ves2.h
    Make sure visiblity works on really high slopes
    Fix precision and crashing for wallmost on high slopes - xcross?
    Screen capture at correct page
    Wall/Floor sprite sorting bugs

  Drawing ideas:
    Make auto-buffer mode turn off with setviewtotile?
    Make lo-detail mode go directly to screen without extra blit somehow?
    Replace getpalookup with buffer Use palookup option for completemirror or use setviewtotile for mirrors
    Allow non 90� viewing ranges

  Movement issues:
    Pushmove for face sprites?
    Make clipmove/getzrange move through parallaxing skies

  Memory issues:
    Allocate sectors and walls dynamically at loadboard

  Speed issues:
    Optimize rotatesprite for weapons, and status bar
    Try fread/fwrite vs. read/write with buffer
    Make hlineasm4 work backwards
    Pre-cache solution - auto cache sprites somehow
    Mergeing optimization (redo in QB)

  Misc. issues:
    Do joining in network games
    Network - send null packets if no packets sent recently
    Split engine into separate modules - drawing, movement, utility

EDITART -
  Seems to corrupt tiles in the artfile after doing a palette grab
  Animation settings don't span art files
$ Tile moving across different art files
$ Make script-making function - F12 every tile and update capfil.txt
$ Offer patch file option after resorting in editart when quitting
> Warn if tile being deleted is used in maps - offer to change.
> Allow comments to be associated with a tile.
> When saving .ART, save to temp, rename, and delete, (int 21h-5ah,56h,41h)
? Screen still goes blank in 'V' mode sometimes with multiple tile files.
B Why doesn't F12 do anything sometimes?

BUILD -
  Goto sector/wall/sprite function
  New walls being added in BUILD should NEVER be masked or blocking
  2D map mode: section off areas to edit overlapping
! Undo
! Sector copy / paste auto-inserting for pasting inside sectors.
> The localtile variables get messed up with 'V' mode inserting/deleteing
> Make overlapping sectors not connect in weird ways
> Type tags in hex
> Print out global visibility in Build status bar
> Information screen (bitmap report, tag report)
> Fix warping in fine sector rotating
> Enter attributes with a menu

GAME -
> Programming example such as deleting sectors, and other BUILD functions
> Fix bugs with fake network player windows.
> Fix message typing (for multiplayer games) when screen is weird sizes

DOCUMENTATION -
> Make PROTOTYPES for functions in GAME.C in BUILD.H.
> Document all variables in BUILD.H

LOW PRIORITY -
> Use multiple palettes other than palette.dat (command line?)
> Fancy shading for torches, flashlights, explosions, etc.
> Real lighting
? Multiple stories - (Not just: the fate of the universe depends on you)
? Add POLYTEX/CAVE stuff - true looking up/down
? Coded sprites for guns and menus

repo_name: orochi/nblood

license: mit


code:
// Assembly-language function wrappers for a.asm functions
// for the Build Engine
// by Jonathon Fowler (jf@jonof.id.au)

#ifndef a_h_
#define a_h_

#include "compat.h"

#define CLASSIC_SLICE_BY_4
#define A_C_RESTRICT __restrict

#define CLASSIC_NONPOW2_YSIZE_SPRITES
#define CLASSIC_NONPOW2_YSIZE_WALLS

#define SLOPTABLESIZ 32768
#define HALFSLOPTABLESIZ (SLOPTABLESIZ>>1)

extern int32_t sloptable[SLOPTABLESIZ];

/** Definitions of high-precision integer types. **/
// Should be used for values that represent coordinates with which calculations
// like dot product are carried out. Substituting 32-bit ints for these will
// very likely yield issues in border cases:
typedef int64_t coord_t;
// Should be used for values that may overflow if 32-bit arithmetic were used,
// but where no other adverse effect (except being undefined behavior,
// obviously) is expected to result:
typedef int64_t inthi_t;
#define inthi_rintf llrintf

#if !defined(NOASM) && (defined(_MSC_VER) || (defined(__GNUC__) && defined(__i386__)))

#if defined __linux || defined __APPLE__ || defined EDUKE32_BSD || defined __SYLLABLE__
#define __cdecl
#endif

#ifdef __cplusplus
extern "C"
{
#endif

extern int32_t __cdecl mmxoverlay();
extern int32_t __cdecl sethlinesizes(int32_t,int32_t,int32_t);
extern int32_t __cdecl setpalookupaddress(char *);
extern int32_t __cdecl setuphlineasm4(int32_t,int32_t);
extern int32_t __cdecl hlineasm4(ssize_t,int32_t,int32_t,int32_t,int32_t,int32_t);
extern int32_t __cdecl setuprhlineasm4(int32_t,int32_t,int32_t,int32_t,int32_t,int32_t);
extern int32_t __cdecl rhlineasm4(int32_t,int32_t,int32_t,int32_t,int32_t,int32_t);
extern int32_t __cdecl setuprmhlineasm4(int32_t,int32_t,int32_t,int32_t,int32_t,int32_t);
extern int32_t __cdecl rmhlineasm4(int32_t,int32_t,int32_t,int32_t,int32_t,int32_t);
extern int32_t __cdecl setupqrhlineasm4(int32_t,int32_t,int32_t,int32_t,int32_t,int32_t);
extern int32_t __cdecl qrhlineasm4(int32_t,int32_t,int32_t,int32_t,int32_t,int32_t);
extern int32_t __cdecl setvlinebpl(int32_t);
extern int32_t __cdecl fixtransluscence(int32_t);
extern int32_t __cdecl prevlineasm1(int32_t,int32_t,int32_t,int32_t,int32_t,int32_t);
extern int32_t __cdecl vlineasm1(int32_t,int32_t,int32_t,int32_t,int32_t,int32_t);
extern int32_t __cdecl vlineasm1nonpow2(int32_t,int32_t,int32_t,int32_t,int32_t,int32_t);
extern int32_t __cdecl setuptvlineasm(int32_t,int32_t);
extern int32_t __cdecl tvlineasm1(int32_t,int32_t,int32_t,int32_t,int32_t,int32_t);
extern int32_t __cdecl tvlineasm1nonpow2(int32_t,int32_t,int32_t,int32_t,int32_t,int32_t);
extern int32_t __cdecl setuptvlineasm2(int32_t,int32_t,int32_t);
extern int32_t __cdecl tvlineasm2(int32_t,int32_t,int32_t,int32_t,int32_t,int32_t);
extern int32_t __cdecl mvlineasm1(int32_t,int32_t,int32_t,int32_t,int32_t,int32_t);
extern int32_t __cdecl mvlineasm1nonpow2(int32_t,int32_t,int32_t,int32_t,int32_t,int32_t);
extern int32_t __cdecl setupvlineasm(int32_t);
extern int32_t __cdecl vlineasm4(int32_t,char *);
extern int32_t __cdecl setupmvlineasm(int32_t,int32_t);
extern int32_t __cdecl mvlineasm4(int32_t,char *);
extern int32_t __cdecl setupspritevline(int32_t,int32_t,int32_t,int32_t,int32_t,int32_t);
extern int32_t __cdecl spritevline(int32_t,int32_t,int32_t,int32_t,int32_t,int32_t);
extern int32_t __cdecl msetupspritevline(int32_t,int32_t,int32_t,int32_t,int32_t,int32_t);
extern int32_t __cdecl mspritevline(int32_t,int32_t,int32_t,int32_t,int32_t,int32_t);
extern int32_t __cdecl tsetupspritevline(int32_t,int32_t,int32_t,int32_t,int32_t,int32_t);
extern int32_t __cdecl tspritevline(int32_t,int32_t,int32_t,int32_t,int32_t,int32_t);
extern int32_t __cdecl mhline(int32_t,int32_t,int32_t,int32_t,int32_t,int32_t);
extern int32_t __cdecl mhlineskipmodify(int32_t,int32_t,int32_t,int32_t,int32_t,int32_t);
extern int32_t __cdecl msethlineshift(int32_t,int32_t);
extern int32_t __cdecl thline(int32_t,int32_t,int32_t,int32_t,int32_t,int32_t);
extern int32_t __cdecl thlineskipmodify(int32_t,int32_t,int32_t,int32_t,int32_t,int32_t);
extern int32_t __cdecl tsethlineshift(int32_t,int32_t);
extern int32_t __cdecl setupslopevlin(int32_t,int32_t,int32_t);
extern int32_t __cdecl slopevlin(int32_t,int32_t,int32_t,int32_t,int32_t,int32_t);
extern int32_t __cdecl settransnormal();
extern int32_t __cdecl settransreverse();
extern int32_t __cdecl setupdrawslab(int32_t,int32_t);
extern int32_t __cdecl drawslab(int32_t,int32_t,int32_t,int32_t,int32_t,int32_t);
extern void __cdecl stretchhline(int32_t,int32_t,int32_t,int32_t,int32_t,int32_t);

#ifdef __cplusplus
}
#endif

#else // !defined(NOASM) && (defined(_MSC_VER) || (defined(__GNUC__) && defined(__i386__)))

#define ENGINE_USING_A_C

#define prevlineasm1 vlineasm1

void setvlinebpl(int32_t dabpl);
void fixtransluscence(intptr_t datransoff);
void settransnormal(void);
void settransreverse(void);

void sethlinesizes(int32_t logx, int32_t logy, intptr_t bufplc);
void setpalookupaddress(char *paladdr);
void setuphlineasm4(int32_t bxinc, int32_t byinc);
void hlineasm4(bssize_t cnt, int32_t skiploadincs, int32_t paloffs, uint32_t by, uint32_t bx, intptr_t p);

void setupslopevlin(int32_t logylogx, intptr_t bufplc, int32_t pinc);
void slopevlin(intptr_t p, int32_t i, intptr_t slopaloffs, bssize_t cnt, int32_t bx, int32_t by);

void setupvlineasm(int32_t neglogy);
int32_t vlineasm1(int32_t vinc, intptr_t paloffs, bssize_t cnt, uint32_t vplc, intptr_t bufplc, intptr_t p);
void vlineasm4(bssize_t cnt, char *p);

void setupmvlineasm(int32_t neglogy, int32_t dosaturate);
int32_t mvlineasm1(int32_t vinc, intptr_t paloffs, bssize_t cnt, uint32_t vplc, intptr_t bufplc, intptr_t p);
void mvlineasm4(bssize_t cnt, char *p);

void setuptvlineasm(int32_t neglogy, int32_t dosaturate);
int32_t tvlineasm1(int32_t vinc, intptr_t paloffs, bssize_t cnt, uint32_t vplc, intptr_t bufplc, intptr_t p);

void setuptvlineasm2(int32_t neglogy, intptr_t paloffs1, intptr_t paloffs2);
void tvlineasm2(uint32_t vplc2, int32_t vinc1, intptr_t bufplc1, intptr_t bufplc2, uint32_t vplc1, intptr_t p);

void msethlineshift(int32_t logx, int32_t logy);
void mhline(intptr_t bufplc, uint32_t bx, int32_t cntup16, int32_t junk, uint32_t by, intptr_t p);

void tsethlineshift(int32_t logx, int32_t logy);
void thline(intptr_t bufplc, uint32_t bx, int32_t cntup16, int32_t junk, uint32_t by, intptr_t p);

void setupspritevline(intptr_t paloffs, int32_t bxinc, int32_t byinc, int32_t ysiz);
void spritevline(int32_t bx, int32_t by, bssize_t cnt, intptr_t bufplc, intptr_t p);

void msetupspritevline(intptr_t paloffs, int32_t bxinc, int32_t byinc, int32_t ysiz);
void mspritevline(int32_t bx, int32_t by, bssize_t cnt, intptr_t bufplc, intptr_t p);

void tsetupspritevline(intptr_t paloffs, int32_t bxinc, int32_t byinc, int32_t ysiz);
void tspritevline(int32_t bx, int32_t by, bssize_t cnt, intptr_t bufplc, intptr_t p);

void setupdrawslab (int32_t dabpl, intptr_t pal);
void drawslab (int32_t dx, int32_t v, int32_t dy, int32_t vi, intptr_t vptr, intptr_t p);
void stretchhline (intptr_t p0, int32_t u, bssize_t cnt, int32_t uinc, intptr_t rptr, intptr_t p);

void mmxoverlay();

#endif	// else

#endif // a_h_


code:
#ifndef USE_OPENGL
# error "VP8 support requires OpenGL"
#endif

#ifndef ANIM_VPX_H
#define ANIM_VPX_H

#define VPX_CODEC_DISABLE_COMPAT 1
#ifndef ANIMVPX_STANDALONE
# undef UNUSED
# include <vpx/vpx_decoder.h>
//#include <vpx/vp8dx.h>
#endif

// IVF format: http://wiki.multimedia.cx/index.php?title=IVF
#pragma pack(push,1)
typedef struct
{
    char magic[4];
    uint16_t version;
    uint16_t hdrlen;

    char fourcc[4];
    uint16_t width;
    uint16_t height;

    uint32_t fpsnumer;
    uint32_t fpsdenom;

    uint32_t numframes;
    uint32_t unused_;
} animvpx_ivf_header_t;
#pragma pack(pop)

#ifndef ANIMVPX_STANDALONE
#include "vfs.h"

extern const char *animvpx_read_ivf_header_errmsg[7];
int32_t animvpx_read_ivf_header(buildvfs_kfd inhandle, animvpx_ivf_header_t *hdr);

typedef struct
{
    const char *errmsg;  // non-NULL if codec error? better always check...
    const char *errmsg_detail;  // may be NULL even if codec error

    uint16_t width, height;
    uint8_t *pic;  // lines of [Y U V 0], calloc'ed on init

    // VVV everything that follows should be considered private! VVV

    buildvfs_kfd inhandle;  // the kread() file handle

    // state of this struct:
    //  0: uninited (either not yet or already)
    //  1: codec init OK
    // -1: error while initing
    // -2: error while uniniting
    int32_t initstate;

    // decoder state:
    //  0: first time / begin
    //  1: have more frames
    //  2: reached EOF
    // -1: unspecified error
    // -2: decoder error
    int32_t decstate;

    uint32_t compbuflen;
    uint32_t compbufallocsiz;
    uint8_t *compbuf;  // compressed data buffer (one IVF/VP8 frame)

    vpx_codec_ctx_t codec;
    vpx_codec_iter_t iter;

    // statistics
    int32_t numframes;
    int32_t sumtimes[3];
    int32_t maxtimes[3];
} animvpx_codec_ctx;

int32_t animvpx_init_codec(const animvpx_ivf_header_t *info, buildvfs_kfd inhandle, animvpx_codec_ctx *codec);
int32_t animvpx_uninit_codec(animvpx_codec_ctx *codec);

extern const char *animvpx_nextpic_errmsg[8];
int32_t animvpx_nextpic(animvpx_codec_ctx *codec, uint8_t **pic);

void animvpx_setup_glstate(int32_t animvpx_flags);
void animvpx_restore_glstate(void);
int32_t animvpx_render_frame(animvpx_codec_ctx *codec, double animvpx_aspect);

void animvpx_print_stats(const animvpx_codec_ctx *codec);
#endif

static inline int32_t animvpx_check_header(const animvpx_ivf_header_t *hdr)
{
    if (Bmemcmp(hdr->magic,"DKIF",4))
        return 2;  // "not an IVF file"

    if (hdr->version != 0)
        return 3;  // "unrecognized IVF version"

    // fourcc is left as-is
    if (Bmemcmp(hdr->fourcc, "VP80", 4))
        return 4;  // "only VP8 supported"

    return 0;
}

#endif  // !defined ANIM_VPX_H


code:
#include "compat.h"
#ifndef atomiclist_h__
#define atomiclist_h__

// ------------------------------------------------------------------
// AtomicSListNode:
// ------------------------------------------------------------------

template<typename T>
struct AtomicSListNode
{
    T * m_next = nullptr;
};

// ------------------------------------------------------------------
// AtomicSList64:
// ------------------------------------------------------------------

// Atomic, but *not* A-B-A safe.
// We can concurrently push and pop, but only as long
// as the incoming pointer is guaranteed to be unique.
// Still covers a lot of usage cases though, such as
// concurrently building up a list that will later be
// consumed by a single thread.
template<typename T>
class AtomicSList64 final
{
public:

    AtomicSList64()
        : m_head{ nullptr }
    { }

    FORCE_INLINE void push(T * node)
    {
        T * currHead = m_head.load(std::memory_order_relaxed);
        do {
            node->m_next = currHead;
        } while (!m_head.compare_exchange_weak(currHead, node,
                                               std::memory_order_release,
                                               std::memory_order_relaxed));
    }

    FORCE_INLINE T * pop()
    {
        T * currHead = m_head.load(std::memory_order_relaxed);
        while (currHead != nullptr)
        {
            if (m_head.compare_exchange_weak(currHead, currHead->m_next,
                                             std::memory_order_release,
                                             std::memory_order_acquire))
            {
                break;
            }
        }
        return currHead;
    }

    // These are non-atomic.
    FORCE_INLINE T * first() const
    {
        return m_head.load(std::memory_order_relaxed);
    }
    FORCE_INLINE bool isEmpty() const
    {
        return first() == nullptr;
    }

    // Syntactic sugar to iterate the list.
    FORCE_INLINE static const T * next(const T * curr) { return curr->m_next; }
    FORCE_INLINE static       T * next(      T * curr) { return curr->m_next; }

    // Not copyable.
    AtomicSList64(const AtomicSList64 &) = delete;
    AtomicSList64 & operator = (const AtomicSList64 &) = delete;

private:

    std::atomic<T *> m_head;
};
#endif // atomiclist_h__


code:
// Base services interface declaration
// for the Build Engine
// by Jonathon Fowler (jf@jonof.id.au)

#pragma once

#ifndef baselayer_h_
#define baselayer_h_

#include "compat.h"
#include "log.h"
#include "osd.h"
#include "timer.h"

#ifdef __cplusplus
extern "C" {
#endif

extern int app_main(int argc, char const * const * argv);
extern const char* AppProperName;
extern const char* AppTechnicalName;

void engineSetupAllocator(void);

#ifdef DEBUGGINGAIDS
# define DEBUG_MASK_DRAWING
extern int32_t g_maskDrawMode;
#endif

#define PRINTF_INITIAL_BUFFER_SIZE 32
#define MSGBOX_PRINTF_MAX          1536

extern char quitevent, appactive;
extern char modechange;
extern char nogl;

extern int32_t vsync;
extern int32_t r_finishbeforeswap;
extern int32_t r_glfinish;
extern int32_t r_borderless;
extern int32_t r_displayindex;

extern void app_crashhandler(void);

// NOTE: these are implemented in game-land so they may be overridden in game specific ways
extern int32_t startwin_open(void);
extern int32_t startwin_close(void);
extern int32_t startwin_puts(const char *);
extern int32_t startwin_settitle(const char *);
extern int32_t startwin_idle(void *);
extern int32_t startwin_run(void);
extern bool startwin_isopen(void);

// video
extern int32_t r_rotatespriteinterp;
extern int32_t r_usenewaspect, newaspect_enable;
extern int32_t r_fpgrouscan;
extern int32_t setaspect_new_use_dimen;
extern uint32_t r_screenxy;
extern int32_t xres, yres, bpp, fullscreen, bytesperline;
extern double refreshfreq;
extern intptr_t frameplace;
extern char offscreenrendering;
extern int32_t nofog;

extern int32_t r_maxfps;
extern int32_t g_numdisplays;
extern int32_t g_displayindex;

extern bool g_ImGuiCaptureInput;
extern bool g_ImGuiFrameActive;
extern uint8_t g_ImGuiCapturedDevices;
extern void engineBeginImGuiFrame(void);
extern void engineEndImGuiInput(void);
extern void engineBeginImGuiInput(void);

void calc_ylookup(int32_t bpl, int32_t lastyidx);

int32_t videoCheckMode(int32_t *x, int32_t *y, int32_t c, int32_t fs, int32_t forced);
int32_t videoSetMode(int32_t x, int32_t y, int32_t c, int32_t fs);
void    videoGetModes(int display = -1);
void    videoResetMode(void);
void    videoEndDrawing(void);
void    videoShowFrame(int32_t);
int32_t videoUpdatePalette(int32_t start, int32_t num);
int32_t videoSetGamma(void);
int32_t videoSetVsync(int32_t newSync);
char const* videoGetDisplayName(int display);
//#define DEBUG_FRAME_LOCKING
#if !defined DEBUG_FRAME_LOCKING
void videoBeginDrawing(void);
#else
void begindrawing_real(void);
# define BEGINDRAWING_SIZE 256
extern uint32_t begindrawing_line[BEGINDRAWING_SIZE];
extern const char *begindrawing_file[BEGINDRAWING_SIZE];
extern int32_t lockcount;
# define videoBeginDrawing() do {                     \
    if (lockcount < BEGINDRAWING_SIZE) {         \
        begindrawing_line[lockcount] = __LINE__; \
        begindrawing_file[lockcount] = __FILE__; \
    }                                            \
    begindrawing_real();                         \
} while(0)
#endif

extern float g_videoGamma, g_videoContrast, g_videoBrightness;

#define DEFAULT_GAMMA 1.0f
#define DEFAULT_CONTRAST 1.0f
#define DEFAULT_BRIGHTNESS 0.0f

#define GAMMA_CALC ((int32_t)(min(max((float)((g_videoGamma - 1.0f) * 10.0f), 0.f), 15.f)))

struct glinfo_t {
    const char *vendor;
    const char *renderer;
    const char *version;
    const char *extensions;

    float maxanisotropy;

    int maxTextureSize;

    int filled;

    union {
        uint32_t features;
        struct
        {
            int bgra             : 1;
            int bufferstorage    : 1;
            int debugoutput      : 1;
            int depthclamp       : 1;
            int depthtex         : 1;
            int fbos             : 1;
            int glsl             : 1;
            int multitex         : 1;
            int occlusionqueries : 1;
            int rect             : 1;
            int reset_notification : 1;
            int samplerobjects   : 1;
            int shadow           : 1;
            int sync             : 1;
            int texcompr         : 1;
            int texnpot          : 1;
            int vsync            : 1;
        };
    };
};

extern struct glinfo_t glinfo;

#ifdef USE_OPENGL
extern int32_t (*baselayer_osdcmd_vidmode_func)(osdcmdptr_t parm);
extern int osdcmd_glinfo(osdcmdptr_t parm);
extern void fill_glinfo(void);
#endif

vec2_t CONSTEXPR const g_defaultVideoModes []
= { { 2560, 1440 }, { 2560, 1200 }, { 2560, 1080 }, { 1920, 1440 }, { 1920, 1200 }, { 1920, 1080 }, { 1680, 1050 },
    { 1600, 1200 }, { 1600, 900 },  { 1366, 768 },  { 1280, 1024 }, { 1280, 960 },  { 1280, 720 },  { 1152, 864 },
    { 1024, 768 },  { 1024, 600 },  { 800, 600 },   { 640, 480 },   { 640, 400 },   { 512, 384 },   { 480, 360 },
    { 400, 300 },   { 320, 240 },   { 320, 200 },   { 0, 0 } };

extern char inputdevices;

#define DEV_KEYBOARD 0x1
#define DEV_MOUSE    0x2
#define DEV_JOYSTICK 0x4

// keys
#define NUMKEYS 256
#define KEYFIFOSIZ 64

char CONSTEXPR const g_keyAsciiTable[128] = {
    0  ,   0,   '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', 0,  0,   'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p',
    '[', ']', 0,   0,   'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', 39, '`', 0,   92,  'z', 'x', 'c', 'v', 'b', 'n', 'm', ',',
    '.', '/', 0,   '*', 0,   32,  0,   0,   0,   0,   0,   0,   0,   0,   0,  0,   0,   0,   0,   '7', '8', '9', '-', '4', '5', '6',
    '+', '1', '2', '3', '0', '.', 0,   0,   0,   0,   0,   0,   0,   0,   0,  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0  ,   0,   0,   0,   0,   0, 0,   0,   0,   0,   0,   0,   0,   0,   0,  0,   0,   0,   0,   0,   0,   0,   0,   0,
};

char CONSTEXPR const g_keyAsciiTableShift[128] = {
    0  ,   0,   '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', 0,  0,   'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P',
    '{', '}', 0,   0,   'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~', 0,   '|',  'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<',
    '>', '?', 0,   '*', 0,   32,  0,   0,   0,   0,   0,   0,   0,   0,   0,  0,   0,   0,   0,   '7', '8', '9', '-', '4', '5', '6',
    '+', '1', '2', '3', '0', '.', 0,   0,   0,   0,   0,   0,   0,   0,   0,  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0  ,   0,   0,   0,   0,   0, 0,   0,   0,   0,   0,   0,   0,   0,   0,  0,   0,   0,   0,   0,   0,   0,   0,   0,
};

extern char    keystatus[NUMKEYS];
extern char    g_keyFIFO[KEYFIFOSIZ];
extern char    g_keyAsciiFIFO[KEYFIFOSIZ];
extern uint8_t g_keyAsciiPos;
extern uint8_t g_keyAsciiEnd;
extern uint8_t g_keyFIFOend;
extern char    g_keyRemapTable[NUMKEYS];
extern char    g_keyNameTable[NUMKEYS][24];

extern int32_t keyGetState(int32_t key);
extern void keySetState(int32_t key, int32_t state);

// mouse
extern vec2_t  g_mousePos;
extern vec2_t  g_mouseAbs;
extern int32_t g_mouseBits;
extern uint8_t g_mouseClickState;
extern bool    g_mouseGrabbed;
extern bool    g_mouseEnabled;
extern bool    g_mouseInsideWindow;
extern bool    g_mouseLockedToWindow;

enum
{
    MOUSE_IDLE = 0,
    MOUSE_PRESSED,
    MOUSE_HELD,
    MOUSE_RELEASED,
};
extern int32_t mouseAdvanceClickState(void);

// joystick

typedef struct
{
    int32_t *pAxis;
    int32_t *pHat;
    void (*pCallback)(int32_t, int32_t);
    int32_t  bits;
    int32_t  numAxes;
    int32_t  numBalls;
    int32_t  numButtons;
    int32_t  numHats;
    uint32_t validButtons;
    uint16_t rumbleLow;
    uint16_t rumbleHigh;
    uint16_t rumbleTime;
    union
    {
        uint8_t flags;
        struct
        {
            unsigned int isGameController : 1;
            unsigned int hasRumble        : 1;
        };
    };
} controllerinput_t;

enum
{
    JOY_CONTROLLER = 0x1,
    JOY_RUMBLE     = 0x2,
};

extern controllerinput_t joystick;

extern int32_t qsetmode;

#define in3dmode() (qsetmode==200)

int32_t initsystem(void);
void uninitsystem(void);
void system_getcvars(void);

void initputs(const char *);
#define buildputs initputs
int initprintf(const char *, ...) ATTRIBUTE((format(printf,1,2)));
#define buildprintf initprintf
int debugprintf(const char *,...) ATTRIBUTE((format(printf,1,2)));

int32_t handleevents(void);
int32_t handleevents_peekkeys(void);

extern void (*keypresscallback)(int32_t,int32_t);
extern void (*g_mouseCallback)(int32_t,int32_t);
extern void (*g_controllerHotplugCallback)(void);

int32_t initinput(void(*hotplugCallback)(void) = NULL);
void uninitinput(void);
void keySetCallback(void (*callback)(int32_t,int32_t));
void mouseSetCallback(void (*callback)(int32_t,int32_t));
void joySetCallback(void (*callback)(int32_t,int32_t));
const char *keyGetName(int32_t num);
const char *joyGetName(int32_t what, int32_t num); // what: 0=axis, 1=button, 2=hat
void joyScanDevices(void);

char keyGetScan(void);
char keyGetChar(void);
#define keyBufferWaiting() (g_keyAsciiPos != g_keyAsciiEnd)

static FORCE_INLINE int keyBufferFull(void)
{
    return ((g_keyAsciiEnd+1)&(KEYFIFOSIZ-1)) == g_keyAsciiPos;
}

static FORCE_INLINE void keyBufferInsert(char code)
{
    g_keyAsciiFIFO[g_keyAsciiEnd] = code;
    g_keyAsciiEnd = ((g_keyAsciiEnd+1)&(KEYFIFOSIZ-1));
}

void keyFlushScans(void);
void keyFlushChars(void);

void mouseInit(void);
void mouseUninit(void);
int32_t mouseReadAbs(vec2_t *pResult, vec2_t const *pInput);
void mouseGrabInput(bool grab);
void mouseLockToWindow(char a);
void mouseMoveToCenter(void);
int32_t mouseReadButtons(void);
void mouseReadPos(int32_t *x, int32_t *y);

bool joyHasButton(int button);
void joyReadButtons(int32_t *pResult);
extern int32_t inputchecked;

int32_t wm_msgbox(const char *name, const char *fmt, ...) ATTRIBUTE((format(printf,2,3)));
int32_t wm_ynbox(const char *name, const char *fmt, ...) ATTRIBUTE((format(printf,2,3)));
void wm_setapptitle(const char *name);

// baselayer.c
int32_t baselayer_init();

void makeasmwriteable(void);
void maybe_redirect_outputs(void);

extern uint64_t g_frameDelay;
static inline uint64_t calcFrameDelay(int maxFPS)
{
    switch (maxFPS)
    {
        case -2: return 0;
        case -1: maxFPS = refreshfreq; break;
        case 0: maxFPS = 1000; break;
    }

    return tabledivide64(timerGetNanoTickRate(), maxFPS);
}
extern int engineFPSLimit(bool const throttle = false);
#ifdef __cplusplus
}
#endif

static inline int32_t calc_smoothratio(ClockTicks const totalclk, ClockTicks const ototalclk, int gameTicRate)
{
    int const   tfreq = (int)floorf(refreshfreq * 120 / timerGetClockRate());
    int const   clk   = (totalclk - ototalclk).toScale16();
    float const tics  = clk * tfreq * (1.f / (65536.f * 120));
    int const   ratio = tabledivide32_noinline((int)(65536 * tics * gameTicRate), tfreq);

    if ((unsigned)ratio > 66048)
        DVLOG_F(LOG_DEBUG+1, "calc_smoothratio: ratio: %d", ratio);

    return clamp(ratio, 0, 65536);
}

static inline void debugThreadName(char const *name)
{
    loguru::set_thread_name(name);

#if defined _WIN32 && !defined NDEBUG
    if (IsDebuggerPresent())
    {
#pragma pack(push, 8)
        typedef struct tagTHREADNAME_INFO
        {
            DWORD  dwType;     /* must be 0x1000 */
            LPCSTR szName;     /* pointer to name (in user addr space) */
            DWORD  dwThreadID; /* thread ID (-1=caller thread) */
            DWORD  dwFlags;    /* reserved for future use, must be zero */
        } THREADNAME_INFO;
#pragma pack(pop)
        THREADNAME_INFO wtf = { 0x1000, name, (DWORD)-1, 0 };
        RaiseException(0x406D1388, 0, sizeof(wtf) / sizeof(ULONG_PTR), (const ULONG_PTR *)&wtf);
    }
#endif
}

#include "print.h"

#endif // baselayer_h_