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:
BUILD SOURCE CODE LICENSE TERMS:                             06/20/2000

[1] I give you permission to make modifications to my Build source and
       distribute it, BUT:

[2] Any derivative works based on my Build source may be distributed ONLY
       through the INTERNET.

[3] Distribution of any derivative works MUST be done completely FREE of
       charge - no commercial exploitation whatsoever.

[4] Anything you distribute which uses a part of my Build Engine source
       code MUST include:

   [A] The following message somewhere in the archive:

       // "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.

   [B] This text file "BUILDLIC.TXT" along with it.

   [C] Any source files that you modify must include this message as well:

       // This file has been modified from Ken Silverman's original release

[5] The use of the Build Engine for commercial purposes will require an
       appropriate license arrangement with me. Contact information is
       on my web site.

[6] I take no responsibility for damage to your system.

[7] Technical support: Before contacting me with questions, please read
       and do ALL of the following!

   [A] Look through ALL of my text files. There are 7 of them (including this
          one). I like to think that I wrote them for a reason. You will find
          many of your answers in the history section of BUILD.TXT and
          BUILD2.TXT (they're located inside SRC.ZIP).

   [B] If that doesn't satisfy you, then try going to:

             "http://www.advsys.net/ken/buildsrc"

          where I will maintain a Build Source Code FAQ (or perhaps I might
          just provide a link to a good FAQ).

   [C] I am willing to respond to questions, but ONLY if they come at a rate
          that I can handle.

             PLEASE TRY TO AVOID ASKING DUPLICATE QUESTIONS!

          As my line of defense, I will post my current policy about
          answering Build source questions (right below the E-mail address
          on my web site.) You can check there to see if I'm getting
          overloaded with questions or not.

          If I'm too busy, it might say something like this:

               I'm too busy to answer Build source questions right now.
               Sorry, but don't expect a reply from me any time soon.

          If I'm open for Build source questions, please state your question
             clearly and don't include any unsolicited attachments unless
             they're really small (like less than 50k). Assume that I have
             a 28.8k modem. Also, don't leave out important details just
             to make your question appear shorter - making me guess what
             you're asking doesn't save me time!

----------------------------------------------------------------------------
-Ken S. (official web site: http://www.advsys.net/ken)

repo_name: orochi/nblood

license: mit


code:
// "Build Engine & Tools" Copyright (c) 1993-1997 Ken Silverman
// Ken Silverman's official web site: "http://www.advsys.net/ken"
// See the included license file "BUILDLIC.TXT" for license info.

#include <dos.h>
#include <stdio.h>
#include "pragmas.h"

//   This module keeps track of a standard linear cacheing system.
//   To use this module, here's all you need to do:
//
//   Step 1: Allocate a nice BIG buffer, like from 1MB-4MB and
//           Call initcache(long cachestart, long cachesize) where
//
//              cachestart = (long)(pointer to start of BIG buffer)
//              cachesize = length of BIG buffer
//
//   Step 2: Call allocache(long *bufptr, long bufsiz, char *lockptr)
//              whenever you need to allocate a buffer, where:
//
//              *bufptr = pointer to 4-byte pointer to buffer
//                 Confused?  Using this method, cache2d can 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.
//
//   Step 3: If you need to remove everything from the cache, or every
//           unlocked item from the cache, you can call uninitcache();
//              Call uninitcache(0) to remove all unlocked items, or
//              Call uninitcache(1) to remove everything.
//           After calling uninitcache, it is still ok to call allocache
//           without first calling initcache.

#define MAXCACHEOBJECTS 9216

static long cachesize = 0;
long cachecount = 0;
char zerochar = 0;
long cachestart = 0, cacnum = 0, agecount = 0;
typedef struct { long *hand, leng; char *lock; } cactype;
cactype cac[MAXCACHEOBJECTS];
static long lockrecip[200];

initcache(long dacachestart, long dacachesize)
{
	long i;

	for(i=1;i<200;i++) lockrecip[i] = (1<<28)/(200-i);

	cachestart = dacachestart;
	cachesize = dacachesize;

	cac[0].leng = cachesize;
	cac[0].lock = &zerochar;
	cacnum = 1;
}

allocache (long *newhandle, long newbytes, char *newlockptr)
{
	long i, j, z, zz, bestz, daval, bestval, besto, o1, o2, sucklen, suckz;

	newbytes = ((newbytes+15)&0xfffffff0);

	if ((unsigned)newbytes > (unsigned)cachesize)
	{
		printf("Cachesize: %ld\n",cachesize);
		printf("*Newhandle: 0x%x, Newbytes: %ld, *Newlock: %d\n",newhandle,newbytes,*newlockptr);
		reportandexit("BUFFER TOO BIG TO FIT IN CACHE!");
	}

	if (*newlockptr == 0)
	{
		reportandexit("ALLOCACHE CALLED WITH LOCK OF 0!");
	}

		//Find best place
	bestval = 0x7fffffff; o1 = cachesize;
	for(z=cacnum-1;z>=0;z--)
	{
		o1 -= cac[z].leng;
		o2 = o1+newbytes; if (o2 > cachesize) continue;

		daval = 0;
		for(i=o1,zz=z;i<o2;i+=cac[zz++].leng)
		{
			if (*cac[zz].lock == 0) continue;
			if (*cac[zz].lock >= 200) { daval = 0x7fffffff; break; }
			daval += mulscale32(cac[zz].leng+65536,lockrecip[*cac[zz].lock]);
			if (daval >= bestval) break;
		}
		if (daval < bestval)
		{
			bestval = daval; besto = o1; bestz = z;
			if (bestval == 0) break;
		}
	}

	//printf("%ld %ld %ld\n",besto,newbytes,*newlockptr);

	if (bestval == 0x7fffffff)
		reportandexit("CACHE SPACE ALL LOCKED UP!");

		//Suck things out
	for(sucklen=-newbytes,suckz=bestz;sucklen<0;sucklen+=cac[suckz++].leng)
		if (*cac[suckz].lock) *cac[suckz].hand = 0;

		//Remove all blocks except 1
	suckz -= (bestz+1); cacnum -= suckz;
	copybufbyte(&cac[bestz+suckz],&cac[bestz],(cacnum-bestz)*sizeof(cactype));
	cac[bestz].hand = newhandle; *newhandle = cachestart+besto;
	cac[bestz].leng = newbytes;
	cac[bestz].lock = newlockptr;
	cachecount++;

		//Add new empty block if necessary
	if (sucklen <= 0) return;

	bestz++;
	if (bestz == cacnum)
	{
		cacnum++; if (cacnum > MAXCACHEOBJECTS) reportandexit("Too many objects in cache! (cacnum > MAXCACHEOBJECTS)");
		cac[bestz].leng = sucklen;
		cac[bestz].lock = &zerochar;
		return;
	}

	if (*cac[bestz].lock == 0) { cac[bestz].leng += sucklen; return; }

	cacnum++; if (cacnum > MAXCACHEOBJECTS) reportandexit("Too many objects in cache! (cacnum > MAXCACHEOBJECTS)");
	for(z=cacnum-1;z>bestz;z--) cac[z] = cac[z-1];
	cac[bestz].leng = sucklen;
	cac[bestz].lock = &zerochar;
}

suckcache (long *suckptr)
{
	long i;

		//Can't exit early, because invalid pointer might be same even though lock = 0
	for(i=0;i<cacnum;i++)
		if ((long)(*cac[i].hand) == (long)suckptr)
		{
			if (*cac[i].lock) *cac[i].hand = 0;
			cac[i].lock = &zerochar;
			cac[i].hand = 0;

				//Combine empty blocks
			if ((i > 0) && (*cac[i-1].lock == 0))
			{
				cac[i-1].leng += cac[i].leng;
				cacnum--; copybuf(&cac[i+1],&cac[i],(cacnum-i)*sizeof(cactype));
			}
			else if ((i < cacnum-1) && (*cac[i+1].lock == 0))
			{
				cac[i+1].leng += cac[i].leng;
				cacnum--; copybuf(&cac[i+1],&cac[i],(cacnum-i)*sizeof(cactype));
			}
		}
}

agecache()
{
	long cnt;
	char ch;

	if (agecount >= cacnum) agecount = cacnum-1;
	for(cnt=(cacnum>>4);cnt>=0;cnt--)
	{
		ch = (*cac[agecount].lock);
		if (((ch-2)&255) < 198)
			(*cac[agecount].lock) = ch-1;

		agecount--; if (agecount < 0) agecount = cacnum-1;
	}
}

reportandexit(char *errormessage)
{
	long i, j;

	setvmode(0x3);
	j = 0;
	for(i=0;i<cacnum;i++)
	{
		printf("%ld- ",i);
		printf("ptr: 0x%x, ",*cac[i].hand);
		printf("leng: %ld, ",cac[i].leng);
		printf("lock: %ld\n",*cac[i].lock);
		j += cac[i].leng;
	}
	printf("Cachesize = %ld\n",cachesize);
	printf("Cacnum = %ld\n",cacnum);
	printf("Cache length sum = %ld\n",j);
	printf("ERROR: %s",errormessage);
	exit(0);
}

#include <fcntl.h>
#include <io.h>
#include <sys\types.h>
#include <sys\stat.h>

#define MAXGROUPFILES 4     //Warning: Fix groupfil if this is changed
#define MAXOPENFILES 64     //Warning: Fix filehan if this is changed

static char toupperlookup[256] =
{
	0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
	0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,
	0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,
	0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,
	0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,
	0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f,
	0x60,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,
	0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x7b,0x7c,0x7d,0x7e,0x7f,
	0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f,
	0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f,
	0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf,
	0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbf,
	0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf,
	0xd0,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdd,0xde,0xdf,
	0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xeb,0xec,0xed,0xee,0xef,
	0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff,
};

static long numgroupfiles = 0;
static long gnumfiles[MAXGROUPFILES];
static long groupfil[MAXGROUPFILES] = {-1,-1,-1,-1};
static long groupfilpos[MAXGROUPFILES];
static char *gfilelist[MAXGROUPFILES];
static long *gfileoffs[MAXGROUPFILES];

static char filegrp[MAXOPENFILES];
static long filepos[MAXOPENFILES];
static long filehan[MAXOPENFILES] =
{
	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
};

initgroupfile(char *filename)
{
	char buf[16];
	long i, j, k;

	if (numgroupfiles >= MAXGROUPFILES) return(-1);

	groupfil[numgroupfiles] = open(filename,O_BINARY|O_RDWR,S_IREAD);
	if (groupfil[numgroupfiles] != -1)
	{
		groupfilpos[numgroupfiles] = 0;
		read(groupfil[numgroupfiles],buf,16);
		if ((buf[0] != 'K') || (buf[1] != 'e') || (buf[2] != 'n') ||
			 (buf[3] != 'S') || (buf[4] != 'i') || (buf[5] != 'l') ||
			 (buf[6] != 'v') || (buf[7] != 'e') || (buf[8] != 'r') ||
			 (buf[9] != 'm') || (buf[10] != 'a') || (buf[11] != 'n'))
		{
			close(groupfil[numgroupfiles]);
			groupfil[numgroupfiles] = -1;
			return(-1);
		}
		gnumfiles[numgroupfiles] = *((long *)&buf[12]);

		if ((gfilelist[numgroupfiles] = (char *)kmalloc(gnumfiles[numgroupfiles]<<4)) == 0)
			{ printf("Not enough memory for file grouping system\n"); exit(0); }
		if ((gfileoffs[numgroupfiles] = (long *)kmalloc((gnumfiles[numgroupfiles]+1)<<2)) == 0)
			{ printf("Not enough memory for file grouping system\n"); exit(0); }

		read(groupfil[numgroupfiles],gfilelist[numgroupfiles],gnumfiles[numgroupfiles]<<4);

		j = 0;
		for(i=0;i<gnumfiles[numgroupfiles];i++)
		{
			k = *((long *)&gfilelist[numgroupfiles][(i<<4)+12]);
			gfilelist[numgroupfiles][(i<<4)+12] = 0;
			gfileoffs[numgroupfiles][i] = j;
			j += k;
		}
		gfileoffs[numgroupfiles][gnumfiles[numgroupfiles]] = j;
	}
	numgroupfiles++;
	return(groupfil[numgroupfiles-1]);
}

uninitgroupfile()
{
	long i;

	for(i=numgroupfiles-1;i>=0;i--)
		if (groupfil[i] != -1)
		{
			kfree(gfilelist[i]);
			kfree(gfileoffs[i]);
			close(groupfil[i]);
			groupfil[i] = -1;
		}
}

kopen4load(char *filename, char searchfirst)
{
	long i, j, k, fil, newhandle;
	char bad, *gfileptr;

	newhandle = MAXOPENFILES-1;
	while (filehan[newhandle] != -1)
	{
		newhandle--;
		if (newhandle < 0)
		{
			printf("TOO MANY FILES OPEN IN FILE GROUPING SYSTEM!");
			exit(0);
		}
	}

	if (searchfirst == 0)
		if ((fil = open(filename,O_BINARY|O_RDONLY)) != -1)
		{
			filegrp[newhandle] = 255;
			filehan[newhandle] = fil;
			filepos[newhandle] = 0;
			return(newhandle);
		}
	for(k=numgroupfiles-1;k>=0;k--)
	{
		if (searchfirst != 0) k = 0;
		if (groupfil[k] != -1)
		{
			for(i=gnumfiles[k]-1;i>=0;i--)
			{
				gfileptr = (char *)&gfilelist[k][i<<4];

				bad = 0;
				for(j=0;j<13;j++)
				{
					if (!filename[j]) break;
					if (toupperlookup[filename[j]] != toupperlookup[gfileptr[j]])
						{ bad = 1; break; }
				}
				if (bad) continue;

				filegrp[newhandle] = k;
				filehan[newhandle] = i;
				filepos[newhandle] = 0;
				return(newhandle);
			}
		}
	}
	return(-1);
}

kread(long handle, void *buffer, long leng)
{
	long i, j, filenum, groupnum;

	filenum = filehan[handle];
	groupnum = filegrp[handle];
	if (groupnum == 255) return(read(filenum,buffer,leng));

	if (groupfil[groupnum] != -1)
	{
		i = gfileoffs[groupnum][filenum]+filepos[handle];
		if (i != groupfilpos[groupnum])
		{
			lseek(groupfil[groupnum],i+((gnumfiles[groupnum]+1)<<4),SEEK_SET);
			groupfilpos[groupnum] = i;
		}
		leng = min(leng,(gfileoffs[groupnum][filenum+1]-gfileoffs[groupnum][filenum])-filepos[handle]);
		leng = read(groupfil[groupnum],buffer,leng);
		filepos[handle] += leng;
		groupfilpos[groupnum] += leng;
		return(leng);
	}

	return(0);
}

klseek(long handle, long offset, long whence)
{
	long i, groupnum;

	groupnum = filegrp[handle];

	if (groupnum == 255) return(lseek(filehan[handle],offset,whence));
	if (groupfil[groupnum] != -1)
	{
		switch(whence)
		{
			case SEEK_SET: filepos[handle] = offset; break;
			case SEEK_END: i = filehan[handle];
								filepos[handle] = (gfileoffs[groupnum][i+1]-gfileoffs[groupnum][i])+offset;
								break;
			case SEEK_CUR: filepos[handle] += offset; break;
		}
		return(filepos[handle]);
	}
	return(-1);
}

kfilelength(long handle)
{
	long i, groupnum;

	groupnum = filegrp[handle];
	if (groupnum == 255) return(filelength(filehan[handle]));
	i = filehan[handle];
	return(gfileoffs[groupnum][i+1]-gfileoffs[groupnum][i]);
}

kclose(long handle)
{
	if (handle < 0) return;
	if (filegrp[handle] == 255) close(filehan[handle]);
	filehan[handle] = -1;
}

	//Internal LZW variables
#define LZWSIZE 16384           //Watch out for shorts!
static char *lzwbuf1, *lzwbuf4, *lzwbuf5, lzwbuflock[5];
static short *lzwbuf2, *lzwbuf3;

kdfread(void *buffer, size_t dasizeof, size_t count, long fil)
{
	long i, j, k, kgoal;
	short leng;
	char *ptr;

	lzwbuflock[0] = lzwbuflock[1] = lzwbuflock[2] = lzwbuflock[3] = lzwbuflock[4] = 200;
	if (lzwbuf1 == NULL) allocache((long *)&lzwbuf1,LZWSIZE+(LZWSIZE>>4),&lzwbuflock[0]);
	if (lzwbuf2 == NULL) allocache((long *)&lzwbuf2,(LZWSIZE+(LZWSIZE>>4))*2,&lzwbuflock[1]);
	if (lzwbuf3 == NULL) allocache((long *)&lzwbuf3,(LZWSIZE+(LZWSIZE>>4))*2,&lzwbuflock[2]);
	if (lzwbuf4 == NULL) allocache((long *)&lzwbuf4,LZWSIZE,&lzwbuflock[3]);
	if (lzwbuf5 == NULL) allocache((long *)&lzwbuf5,LZWSIZE+(LZWSIZE>>4),&lzwbuflock[4]);

	if (dasizeof > LZWSIZE) { count *= dasizeof; dasizeof = 1; }
	ptr = (char *)buffer;

	kread(fil,&leng,2); kread(fil,lzwbuf5,(long)leng);
	k = 0; kgoal = uncompress(lzwbuf5,(long)leng,lzwbuf4);

	copybufbyte(lzwbuf4,ptr,(long)dasizeof);
	k += (long)dasizeof;

	for(i=1;i<count;i++)
	{
		if (k >= kgoal)
		{
			kread(fil,&leng,2); kread(fil,lzwbuf5,(long)leng);
			k = 0; kgoal = uncompress(lzwbuf5,(long)leng,lzwbuf4);
		}
		for(j=0;j<dasizeof;j++) ptr[j+dasizeof] = ((ptr[j]+lzwbuf4[j+k])&255);
		k += dasizeof;
		ptr += dasizeof;
	}
	lzwbuflock[0] = lzwbuflock[1] = lzwbuflock[2] = lzwbuflock[3] = lzwbuflock[4] = 1;
}

dfread(void *buffer, size_t dasizeof, size_t count, FILE *fil)
{
	long i, j, k, kgoal;
	short leng;
	char *ptr;

	lzwbuflock[0] = lzwbuflock[1] = lzwbuflock[2] = lzwbuflock[3] = lzwbuflock[4] = 200;
	if (lzwbuf1 == NULL) allocache((long *)&lzwbuf1,LZWSIZE+(LZWSIZE>>4),&lzwbuflock[0]);
	if (lzwbuf2 == NULL) allocache((long *)&lzwbuf2,(LZWSIZE+(LZWSIZE>>4))*2,&lzwbuflock[1]);
	if (lzwbuf3 == NULL) allocache((long *)&lzwbuf3,(LZWSIZE+(LZWSIZE>>4))*2,&lzwbuflock[2]);
	if (lzwbuf4 == NULL) allocache((long *)&lzwbuf4,LZWSIZE,&lzwbuflock[3]);
	if (lzwbuf5 == NULL) allocache((long *)&lzwbuf5,LZWSIZE+(LZWSIZE>>4),&lzwbuflock[4]);

	if (dasizeof > LZWSIZE) { count *= dasizeof; dasizeof = 1; }
	ptr = (char *)buffer;

	fread(&leng,2,1,fil); fread(lzwbuf5,(long)leng,1,fil);
	k = 0; kgoal = uncompress(lzwbuf5,(long)leng,lzwbuf4);

	copybufbyte(lzwbuf4,ptr,(long)dasizeof);
	k += (long)dasizeof;

	for(i=1;i<count;i++)
	{
		if (k >= kgoal)
		{
			fread(&leng,2,1,fil); fread(lzwbuf5,(long)leng,1,fil);
			k = 0; kgoal = uncompress(lzwbuf5,(long)leng,lzwbuf4);
		}
		for(j=0;j<dasizeof;j++) ptr[j+dasizeof] = ((ptr[j]+lzwbuf4[j+k])&255);
		k += dasizeof;
		ptr += dasizeof;
	}
	lzwbuflock[0] = lzwbuflock[1] = lzwbuflock[2] = lzwbuflock[3] = lzwbuflock[4] = 1;
}

dfwrite(void *buffer, size_t dasizeof, size_t count, FILE *fil)
{
	long i, j, k;
	short leng;
	char *ptr;

	lzwbuflock[0] = lzwbuflock[1] = lzwbuflock[2] = lzwbuflock[3] = lzwbuflock[4] = 200;
	if (lzwbuf1 == NULL) allocache((long *)&lzwbuf1,LZWSIZE+(LZWSIZE>>4),&lzwbuflock);
	if (lzwbuf2 == NULL) allocache((long *)&lzwbuf2,(LZWSIZE+(LZWSIZE>>4))*2,&lzwbuflock);
	if (lzwbuf3 == NULL) allocache((long *)&lzwbuf3,(LZWSIZE+(LZWSIZE>>4))*2,&lzwbuflock);
	if (lzwbuf4 == NULL) allocache((long *)&lzwbuf4,LZWSIZE,&lzwbuflock);
	if (lzwbuf5 == NULL) allocache((long *)&lzwbuf5,LZWSIZE+(LZWSIZE>>4),&lzwbuflock);

	if (dasizeof > LZWSIZE) { count *= dasizeof; dasizeof = 1; }
	ptr = (char *)buffer;

	copybufbyte(ptr,lzwbuf4,(long)dasizeof);
	k = dasizeof;

	if (k > LZWSIZE-dasizeof)
	{
		leng = (short)compress(lzwbuf4,k,lzwbuf5); k = 0;
		fwrite(&leng,2,1,fil); fwrite(lzwbuf5,(long)leng,1,fil);
	}

	for(i=1;i<count;i++)
	{
		for(j=0;j<dasizeof;j++) lzwbuf4[j+k] = ((ptr[j+dasizeof]-ptr[j])&255);
		k += dasizeof;
		if (k > LZWSIZE-dasizeof)
		{
			leng = (short)compress(lzwbuf4,k,lzwbuf5); k = 0;
			fwrite(&leng,2,1,fil); fwrite(lzwbuf5,(long)leng,1,fil);
		}
		ptr += dasizeof;
	}
	if (k > 0)
	{
		leng = (short)compress(lzwbuf4,k,lzwbuf5);
		fwrite(&leng,2,1,fil); fwrite(lzwbuf5,(long)leng,1,fil);
	}
	lzwbuflock[0] = lzwbuflock[1] = lzwbuflock[2] = lzwbuflock[3] = lzwbuflock[4] = 1;
}

compress(char *lzwinbuf, long uncompleng, char *lzwoutbuf)
{
	long i, addr, newaddr, addrcnt, zx, *longptr;
	long bytecnt1, bitcnt, numbits, oneupnumbits;
	short *shortptr;

	for(i=255;i>=0;i--) { lzwbuf1[i] = i; lzwbuf3[i] = (i+1)&255; }
	clearbuf(FP_OFF(lzwbuf2),256>>1,0xffffffff);
	clearbuf(FP_OFF(lzwoutbuf),((uncompleng+15)+3)>>2,0L);

	addrcnt = 256; bytecnt1 = 0; bitcnt = (4<<3);
	numbits = 8; oneupnumbits = (1<<8);
	do
	{
		addr = lzwinbuf[bytecnt1];
		do
		{
			bytecnt1++;
			if (bytecnt1 == uncompleng) break;
			if (lzwbuf2[addr] < 0) {lzwbuf2[addr] = addrcnt; break;}
			newaddr = lzwbuf2[addr];
			while (lzwbuf1[newaddr] != lzwinbuf[bytecnt1])
			{
				zx = lzwbuf3[newaddr];
				if (zx < 0) {lzwbuf3[newaddr] = addrcnt; break;}
				newaddr = zx;
			}
			if (lzwbuf3[newaddr] == addrcnt) break;
			addr = newaddr;
		} while (addr >= 0);
		lzwbuf1[addrcnt] = lzwinbuf[bytecnt1];
		lzwbuf2[addrcnt] = -1;
		lzwbuf3[addrcnt] = -1;

		longptr = (long *)&lzwoutbuf[bitcnt>>3];
		longptr[0] |= (addr<<(bitcnt&7));
		bitcnt += numbits;
		if ((addr&((oneupnumbits>>1)-1)) > ((addrcnt-1)&((oneupnumbits>>1)-1)))
			bitcnt--;

		addrcnt++;
		if (addrcnt > oneupnumbits) { numbits++; oneupnumbits <<= 1; }
	} while ((bytecnt1 < uncompleng) && (bitcnt < (uncompleng<<3)));

	longptr = (long *)&lzwoutbuf[bitcnt>>3];
	longptr[0] |= (addr<<(bitcnt&7));
	bitcnt += numbits;
	if ((addr&((oneupnumbits>>1)-1)) > ((addrcnt-1)&((oneupnumbits>>1)-1)))
		bitcnt--;

	shortptr = (short *)lzwoutbuf;
	shortptr[0] = (short)uncompleng;
	if (((bitcnt+7)>>3) < uncompleng)
	{
		shortptr[1] = (short)addrcnt;
		return((bitcnt+7)>>3);
	}
	shortptr[1] = (short)0;
	for(i=0;i<uncompleng;i++) lzwoutbuf[i+4] = lzwinbuf[i];
	return(uncompleng+4);
}

uncompress(char *lzwinbuf, long compleng, char *lzwoutbuf)
{
	long strtot, currstr, numbits, oneupnumbits;
	long i, dat, leng, bitcnt, outbytecnt, *longptr;
	short *shortptr;

	shortptr = (short *)lzwinbuf;
	strtot = (long)shortptr[1];
	if (strtot == 0)
	{
		copybuf(FP_OFF(lzwinbuf)+4,FP_OFF(lzwoutbuf),((compleng-4)+3)>>2);
		return((long)shortptr[0]); //uncompleng
	}
	for(i=255;i>=0;i--) { lzwbuf2[i] = i; lzwbuf3[i] = i; }
	currstr = 256; bitcnt = (4<<3); outbytecnt = 0;
	numbits = 8; oneupnumbits = (1<<8);
	do
	{
		longptr = (long *)&lzwinbuf[bitcnt>>3];
		dat = ((longptr[0]>>(bitcnt&7)) & (oneupnumbits-1));
		bitcnt += numbits;
		if ((dat&((oneupnumbits>>1)-1)) > ((currstr-1)&((oneupnumbits>>1)-1)))
			{ dat &= ((oneupnumbits>>1)-1); bitcnt--; }

		lzwbuf3[currstr] = dat;

		for(leng=0;dat>=256;leng++,dat=lzwbuf3[dat])
			lzwbuf1[leng] = lzwbuf2[dat];

		lzwoutbuf[outbytecnt++] = dat;
		for(i=leng-1;i>=0;i--) lzwoutbuf[outbytecnt++] = lzwbuf1[i];

		lzwbuf2[currstr-1] = dat; lzwbuf2[currstr] = dat;
		currstr++;
		if (currstr > oneupnumbits) { numbits++; oneupnumbits <<= 1; }
	} while (currstr < strtot);
	return((long)shortptr[0]); //uncompleng
}