//-----------------------------------------------------------------------------
//
//  $Logfile:: /Source/sv_game.c                                              $
// $Revision:: 4                                                              $
//   $Author:: Burgermac                                                      $
//     $Date:: 8/10/01 6:01 PM                                                $
//
// Copyright (C) 1998 by Ritual Entertainment, Inc.
// All rights reserved.
//
// This source is may not be distributed and/or modified without
// expressly written permission by Ritual Entertainment, Inc.
//
// DESCRIPTION:
// interface to the game dll
//

#include "server.h"
#include "sinmdl.h"
#include "alias.h"
#include "wave.h"
#include "crc.h"

game_export_t	*ge;

debugline_t		*DebugLines = NULL;
int				numDebugLines = 0;


/*
===============
PF_Unicast

Sends the contents of the mutlicast buffer to a single client
===============
*/
static void PF_Unicast (edict_t *ent, qboolean reliable)
{
	int		p;
	client_t	*client;

	if (!ent)
		return;

	p = NUM_FOR_EDICT(ent);
	if (p < 1 || p > maxclients->value)
		return;

	client = svs.clients + (p-1);

	if (reliable)
		SZ_Write (&client->netchan.message, sv.multicast.data, sv.multicast.cursize);
	else
		SZ_Write (&client->datagram, sv.multicast.data, sv.multicast.cursize);

	SZ_Clear (&sv.multicast);
}


/*
===============
PF_dprintf

Debug print to server console
===============
*/
static void __attribute__((__format__(__printf__,1,2)))
PF_dprintf (const char *fmt, ...)
{
	char		msg[1024];
	va_list		argptr;

	va_start (argptr,fmt);
	Q_vsnprintf (msg, sizeof(msg), fmt, argptr);
	va_end (argptr);
	msg[sizeof(msg)-1] = 0;

	Com_DPrintf (DEVELOPER_MSG_GAME, "%s", msg); /* FS */
}

/*
===============
PF_printf

print to server console
===============
*/
static void __attribute__((__format__(__printf__,1,2)))
PF_printf (const char *fmt, ...)
{
	char		msg[1024];
	va_list		argptr;

	va_start (argptr,fmt);
	Q_vsnprintf (msg, sizeof(msg), fmt, argptr);
	va_end (argptr);
	msg[sizeof(msg)-1] = 0;

	Com_Printf ("%s", msg);
}

/*
===============
PF_cprintf

Print to a single client
===============
*/
static void __attribute__((__format__(__printf__,3,4)))
PF_cprintf (edict_t *ent, int level, const char *fmt, ...)
{
	char		msg[1024];
	va_list		argptr;
	int			n;

	if (ent)
	{
		n = NUM_FOR_EDICT(ent);
		if (n < 1 || n > maxclients->value)
		{
			Com_DPrintf (DEVELOPER_MSG_GAME, "cprintf to a non-client");
			return;
		}
	}
	else {
		n = 0; /* silence pesky compiler warnings. */
	}

	va_start (argptr,fmt);
	Q_vsnprintf (msg, sizeof(msg), fmt, argptr);
	va_end (argptr);
	msg[sizeof(msg)-1] = 0;

	if (ent)
		SV_ClientPrintf (svs.clients+(n-1), level, "%s", msg);
	else
		Com_Printf ("%s", msg);
}


/*
===============
PF_centerprintf

centerprint to a single client
===============
*/
static void __attribute__((__format__(__printf__,2,3)))
PF_centerprintf (edict_t *ent, const char *fmt, ...)
{
	char		msg[1024];
	va_list		argptr;
	int			n;

	n = NUM_FOR_EDICT(ent);
	if (n < 1 || n > maxclients->value)
		return;	// Com_Error (ERR_DROP, "centerprintf to a non-client");

	va_start (argptr,fmt);
	Q_vsnprintf (msg, sizeof(msg), fmt, argptr);
	va_end (argptr);
	msg[sizeof(msg)-1] = 0;

	MSG_WriteByte (&sv.multicast,svc_centerprint);
	MSG_WriteString (&sv.multicast,msg);
	PF_Unicast (ent, true);
}


/*
===============
PF_error

Abort the server with a game error
===============
*/
static void __attribute__((__noreturn__, __format__(__printf__,1,2)))
PF_error (const char *fmt, ...)
{
	char		msg[1024];
	va_list		argptr;

	va_start (argptr,fmt);
	Q_vsnprintf (msg, sizeof(msg), fmt, argptr);
	va_end (argptr);
	msg[sizeof(msg)-1] = 0;

	Com_Error (ERR_DROP, "Game Error: %s", msg);
}

static int SINMDL_handles[MAX_MODELS];

int SINMDL_Handle_For_ModelIndex (int index)
{
	int handle;
	const char *s;
	char model_name[MAX_OSPATH];

	s = sv.configstrings[CS_MODELS+index];
	strcpy(model_name, COM_Parse(&s));

	handle = SINMDL_handles[index];
	if (!handle)
		return -1;

	handle--;

	if (SINMDL_DefHandleValid(handle, model_name))
	{
		return handle;
	}
	else
	{
		handle = SINMDL_RegisterDef(model_name);
		SINMDL_handles[index] = handle + 1;
		return handle;
	}
}

/*
=================
PF_IsModel
Is this a SINMDL?
=================
*/
static qboolean PF_IsModel(int index)
{
	return (SINMDL_handles[index] != 0);
}

/*
=================
PF_NumAnims
Get the number of animations in this model
=================
*/
static int PF_NumAnims(int modelindex)
{
	int defhandle;

	defhandle = SINMDL_Handle_For_ModelIndex(modelindex);
	if (defhandle >= 0)
		return SINMDL_NumAnims(defhandle);

	return 0;
}

/*
=================
PF_NumSkins
Get the number of skins in this model
=================
*/
static int PF_NumSkins(int modelindex)
{
	int defhandle;

	defhandle = SINMDL_Handle_For_ModelIndex(modelindex);
	if (defhandle >= 0)
		return SINMDL_NumSkins(defhandle);

	return 0;
}

/*
=================
PF_NumGroups
Get the number of groups in this model
=================
*/
static int PF_NumGroups(int modelindex)
{
	int defhandle;

	defhandle = SINMDL_Handle_For_ModelIndex(modelindex);
	if (defhandle >= 0)
		return SINMDL_NumGroups(defhandle);

	return 0;
}

/*
=================
PF_GroupFlags
Get the number of groups in this model
=================
*/
static int PF_GroupFlags(int modelindex, int groupindex)
{
	int defhandle;

	defhandle = SINMDL_Handle_For_ModelIndex(modelindex);
	if (defhandle >= 0)
		return SINMDL_Group_Flags(defhandle, groupindex);

	return 0;
}

/*
=================
PF_InitCommands
Get the initialization commands for this model
=================
*/
static sinmdl_cmd_t *PF_InitCommands(int modelindex)
{
	int defhandle;

	defhandle = SINMDL_Handle_For_ModelIndex(modelindex);
	if (defhandle >= 0)
		return SINMDL_InitCommands_Server(defhandle);

	return NULL;
}

/*
=================
PF_CalculateBounds
Calculate the mins and maxs bounding box for this model
=================
*/
static void PF_CalculateBounds(int modelindex, float scale, vec3_t mins, vec3_t maxs)
{
	int defhandle;

	defhandle = SINMDL_Handle_For_ModelIndex(modelindex);
	if (defhandle >= 0)
		SINMDL_CalculateBounds(defhandle, scale, mins, maxs);
}

/*
=================
PF_Anim_NameForNum
Get the name for this animation number
=================
*/
static const char * PF_Anim_NameForNum(int modelindex, int animnum)
{
	int defhandle;

	defhandle = SINMDL_Handle_For_ModelIndex(modelindex);
	if (defhandle >= 0)
		return SINMDL_Anim_NameForNum(defhandle, animnum);

	return NULL;
}

/*
=================
PF_Anim_NumForName
Get the number for this name
=================
*/
static int PF_Anim_NumForName(int modelindex, const char *name)
{
	int defhandle;

	defhandle = SINMDL_Handle_For_ModelIndex(modelindex);
	if (defhandle >= 0)
		return SINMDL_Anim_NumForName(defhandle, name);

	return -1;
}

/*
=================
PF_Anim_Random
Get a random animation
=================
*/
static int PF_Anim_Random(int modelindex, const char *name)
{
	int defhandle;

	defhandle = SINMDL_Handle_For_ModelIndex(modelindex);
	if (defhandle >= 0)
		return SINMDL_Anim_Random(defhandle, name);

	return -1;
}

/*
=================
PF_Anim_NumFrames
Number of frames in this animation
=================
*/
static int PF_Anim_NumFrames(int modelindex, int animnum)
{
	int defhandle;

	defhandle = SINMDL_Handle_For_ModelIndex(modelindex);
	if (defhandle >= 0)
		return SINMDL_Anim_NumFrames(defhandle, animnum);

	return 0;
}

/*
=================
PF_Anim_Time
Time of full animation
=================
*/
static float PF_Anim_Time(int modelindex, int animnum)
{
	int defhandle;

	defhandle = SINMDL_Handle_For_ModelIndex(modelindex);
	if (defhandle >= 0)
		return SINMDL_Anim_Time(defhandle, animnum);

	return 0.0f;
}

/*
=================
PF_Anim_Delta
Total delta for this animation
=================
*/
static void PF_Anim_Delta(int modelindex, int animnum, vec3_t delta)
{
	int defhandle;

	defhandle = SINMDL_Handle_For_ModelIndex(modelindex);
	if (defhandle >= 0)
		SINMDL_Anim_Delta(defhandle, animnum, delta);
}

/*
=================
PF_Frame_Commands
Get the commands for this frame
=================
*/
static sinmdl_cmd_t *PF_Frame_Commands(int modelindex, int animnum, int framenum)
{
	int defhandle;

	defhandle = SINMDL_Handle_For_ModelIndex(modelindex);
	if (defhandle >= 0)
		return SINMDL_Frame_Commands_Server(defhandle, animnum, framenum);

	return NULL;
}

/*
=================
PF_Frame_Time
Time of this frame
=================
*/
static float PF_Frame_Time(int modelindex, int animnum, int framenum)
{
	int defhandle;

	defhandle = SINMDL_Handle_For_ModelIndex(modelindex);
	if (defhandle >= 0)
		return SINMDL_Frame_Time(defhandle, animnum, framenum);

	return 0.0f;
}

/*
=================
PF_Frame_Delta
delta for this frame
=================
*/
static void PF_Frame_Delta(int modelindex, int animnum, int framenum, vec3_t delta)
{
	int defhandle;

	defhandle = SINMDL_Handle_For_ModelIndex(modelindex);
	if (defhandle >= 0)
		SINMDL_Frame_Delta(defhandle, animnum, framenum, delta);
}

/*
=================
PF_Skin_NameForNum
Get the name for this Skin
=================
*/
static const char *PF_Skin_NameForNum(int modelindex, int skinnum)
{
	int defhandle;

	defhandle = SINMDL_Handle_For_ModelIndex(modelindex);
	if (defhandle >= 0)
		return SINMDL_Skin_NameForNum(defhandle, skinnum);

	return NULL;
}

/*
=================
PF_Skin_NumForName
Get the number for this name
=================
*/
static int PF_Skin_NumForName(int modelindex, const char *name)
{
	int defhandle;

	defhandle = SINMDL_Handle_For_ModelIndex(modelindex);
	if (defhandle >= 0)
		return SINMDL_Skin_NumForName(defhandle, name);

	return -1;
}

/*
=================
PF_Group_NameForNum
Get the name for this Group
=================
*/
static const char *PF_Group_NameForNum(int modelindex, int groupnum)
{
	int defhandle;

	defhandle = SINMDL_Handle_For_ModelIndex(modelindex);
	if (defhandle >= 0)
		return SINMDL_Group_NumToName(defhandle, groupnum);

	return NULL;
}

/*
=================
PF_Group_NumForName
Get the number for this name
=================
*/
static int PF_Group_NumForName(int modelindex, const char *name)
{
	int defhandle;

	defhandle = SINMDL_Handle_For_ModelIndex(modelindex);
	if (defhandle >= 0)
		return SINMDL_Group_NameToNum(defhandle, name);

	return -1;
}

/*
=================
PF_Group_DamageMultiplier
DamageMultiplier for this group
=================
*/
static float PF_Group_DamageMultiplier(int modelindex, int groupnum)
{
	int defhandle;

	defhandle = SINMDL_Handle_For_ModelIndex(modelindex);
	if (defhandle >= 0)
		return SINMDL_Group_DamageMultiplier(defhandle, groupnum);

	return 0.0f;
}

/*
=================
PF_GetBoneInfo
Get the bone info for the named bone
=================
*/
static qboolean PF_GetBoneInfo(int modelindex, const char *bonename, int *groupindex, int *tri_num, vec3_t orientation)
{
	int defhandle;

	defhandle = SINMDL_Handle_For_ModelIndex(modelindex);
	if (defhandle >= 0)
		return SINMDL_GetBoneInfo(defhandle, bonename, groupindex, tri_num, orientation);

	return false;
}

/*
=================
PF_GetBoneGroupName
Get the bone's group name
=================
*/
static const char *PF_GetBoneGroupName(int modelindex, const char *bonename)
{
	int defhandle;

	defhandle = SINMDL_Handle_For_ModelIndex(modelindex);
	if (defhandle >= 0)
		return SINMDL_GetBoneGroupName(defhandle, bonename);

	return NULL;
}

/*
=================
PF_GetBoneTransform
Get the bone transform for the named bone
=================
*/
static qboolean PF_GetBoneTransform(int modelindex, int groupindex, int tri_num, vec3_t orientation, int anim, int frame, float scale, vec3_t trans[3], vec3_t pos)
{
	int defhandle;

	defhandle = SINMDL_Handle_For_ModelIndex(modelindex);
	if (defhandle >= 0)
		return SINMDL_GetBoneTransform(defhandle, groupindex, tri_num, orientation, anim, frame, scale, trans, pos);

	return false;
}

/*
=================
PF_Alias_Add
=================
*/
static qboolean PF_Alias_Add(int modelindex, const char *alias, const char *name, float weight)
{
	int defhandle;

	defhandle = SINMDL_Handle_For_ModelIndex(modelindex);
	if (defhandle >= 0)
		return SINMDL_Alias_Add(defhandle, alias, name, weight);

	return false;
}

/*
=================
PF_Alias_FindRandom
=================
*/
static const char *PF_Alias_FindRandom(int modelindex, const char *alias)
{
	int defhandle;

	defhandle = SINMDL_Handle_For_ModelIndex(modelindex);
	if (defhandle >= 0)
		return SINMDL_Alias_FindRandom(defhandle, alias);

	return NULL;
}

/*
=================
PF_Alias_Dump
=================
*/
static void PF_Alias_Dump(int modelindex)
{
	int defhandle;

	defhandle = SINMDL_Handle_For_ModelIndex(modelindex);
	if (defhandle >= 0)
		SINMDL_Alias_Dump(defhandle);
}

/*
=================
PF_Alias_Clear
=================
*/
static void PF_Alias_Clear(int modelindex)
{
	int defhandle;

	defhandle = SINMDL_Handle_For_ModelIndex(modelindex);
	if (defhandle >= 0)
		SINMDL_Alias_Dump(defhandle);
}

/*
=================
PF_modelindex
=================
*/
static int PF_modelindex (const char *name)
{
	int		i;
	size_t	sz;
#ifdef SIN_CHECKSUM
	int handle;
	unsigned checksum;
	char checksumname[MAX_QPATH];

	sz = strlen(name);
	if (sz >= 3 && !Q_stricmp(&name[sz - 3], "def"))
	{
		handle = (SINMDL_RegisterDef(name) + 1);
		checksum = SINMDL_Checksum(handle - 1);
		Com_sprintf(checksumname, sizeof(checksumname), "%s %x", name, checksum);
		i = SV_ModelIndex (checksumname);
		if (!SINMDL_handles[i])
			SINMDL_handles[i] = handle;
	}
	else
	{
		i = SV_ModelIndex (name);
		SINMDL_handles[i] = 0;
	}

	return i;
#else
	i = SV_ModelIndex (name);

	sz = strlen(name);
	if (sz >= 3 && !Q_stricmp(&name[sz - 3], "def"))
	{
		if (!SINMDL_handles[i])
			SINMDL_handles[i] = (SINMDL_RegisterDef(name) + 1);
	}
	else
	{
		SINMDL_handles[i] = 0;
	}

	return i;
#endif
}

/*
=================
PF_setmodel

Also sets mins and maxs for inline bmodels
=================
*/
static void PF_setmodel (edict_t *ent, const char *name)
{
	int		i;
	cmodel_t	*mod;

	if (!name)
		Com_Error (ERR_DROP, "PF_setmodel: NULL");

	i = PF_modelindex(name);

//	ent->model = name;
	ent->s.modelindex = i;

// if it is an inline model, get the size information for it
	if (name[0] == '*')
	{
		mod = CM_InlineModel (name);
		VectorCopy (mod->mins, ent->mins);
		VectorCopy (mod->maxs, ent->maxs);
		VectorCopy (mod->mins, ent->fullmins);
		VectorCopy (mod->maxs, ent->fullmaxs);
		SV_LinkEdict (ent);
	}

}

/*
===============
PF_Configstring

===============
*/
static void PF_Configstring (int index, const char *val)
{
	qboolean changed = false;

	if (index < 0 || index >= MAX_CONFIGSTRINGS)
		Com_Error (ERR_DROP, "configstring: bad index %i\n", index);

	if (!val)
		val = "";

	// change the string in sv
	// The statusbar reserves a group of configstrings
	if ((index > CS_STATUSBAR) && (index < CS_MAPCHECKSUM))
	{
		Com_Error(ERR_FATAL, "configstring #%d, %s is using a reserved configstring\n", index, val);
	}

	// The status bar overwrites a number of configstrings so don't truncate it
	if (index == CS_STATUSBAR)
	{
		if (strcmp(sv.configstrings[index], val))
		{
			changed = true;
			strcpy (sv.configstrings[index], val);
		}
	}
	else
	{
		// Check for overflowing the configstring
		if (strlen(val) > MAX_QPATH)
		{
			Com_DPrintf (DEVELOPER_MSG_NET, "configstring #%d, %s > MAX_QPATH, truncating.\n", index, val);
		}
		if (strcmp( sv.configstrings[index], val))
		{
			changed = true;
			strncpy (sv.configstrings[index], val, MAX_QPATH);
		}
	}

	if (sv.state != ss_loading && changed)
	{	// send the update to everyone
		SZ_Clear (&sv.multicast);
		MSG_WriteChar (&sv.multicast, svc_configstring);
		MSG_WriteShort (&sv.multicast, index);
		MSG_WriteString (&sv.multicast, val);

		SV_Multicast (vec3_origin, MULTICAST_ALL_R);
	}
}



static void PF_WriteChar (int c) {MSG_WriteChar (&sv.multicast, c);}
static void PF_WriteByte (int c) {MSG_WriteByte (&sv.multicast, c);}
static void PF_WriteShort (int c) {MSG_WriteShort (&sv.multicast, c);}
static void PF_WriteLong (int c) {MSG_WriteLong (&sv.multicast, c);}
static void PF_WriteFloat (float f) {MSG_WriteFloat (&sv.multicast, f);}
static void PF_WriteString (const char *s) {MSG_WriteString (&sv.multicast, s);}
static void PF_WritePos (const vec3_t pos) {MSG_WritePos (&sv.multicast, pos);}
static void PF_WriteDir (vec3_t dir) {MSG_WriteDir (&sv.multicast, dir);}
static void PF_WriteAngle (float f) {MSG_WriteAngle (&sv.multicast, f);}


/*
=================
PF_inPVS

Also checks portalareas so that doors block sight
=================
*/
static qboolean PF_inPVS (vec3_t p1, vec3_t p2)
{
	int		leafnum;
	int		cluster;
	int		area1, area2;
	byte	*mask;

	leafnum = CM_PointLeafnum (p1);
	cluster = CM_LeafCluster (leafnum);
	area1 = CM_LeafArea (leafnum);
	mask = CM_ClusterPVS (cluster);

	leafnum = CM_PointLeafnum (p2);
	cluster = CM_LeafCluster (leafnum);
	area2 = CM_LeafArea (leafnum);
	if ( mask && !(mask[cluster>>3] & (1<<(cluster&7))) )
		return false;
	if (!CM_AreasConnected (area1, area2))
		return false;		// a door blocks sight
	return true;
}


/*
=================
PF_inPHS

Also checks portalareas so that doors block sound
=================
*/
static qboolean PF_inPHS (const vec3_t p1,const vec3_t p2)
{
	int		leafnum;
	int		cluster;
	int		area1, area2;
	byte	*mask;

	leafnum = CM_PointLeafnum (p1);
	cluster = CM_LeafCluster (leafnum);
	area1 = CM_LeafArea (leafnum);
	mask = CM_ClusterPHS (cluster);

	leafnum = CM_PointLeafnum (p2);
	cluster = CM_LeafCluster (leafnum);
	area2 = CM_LeafArea (leafnum);
	if ( mask && !(mask[cluster>>3] & (1<<(cluster&7))) )
		return false;		// more than one bounce away
	if (!CM_AreasConnected (area1, area2))
		return false;		// a door blocks hearing

	return true;
}

static void PF_StartSound (edict_t *entity, int channel, int sound_num, float volume,
			   float attenuation, float timeofs, float pitch, float fadetime,
			   int flags)
{
	if (!entity || !EDICT_NUM(0)->inuse)
		return;

	SV_StartSound (NULL, entity, channel, sound_num, volume, attenuation, timeofs, pitch, fadetime, flags);
}

static int PF_LoadFile(const char *path,void **buffer,int tag)
{
	FILE *f = NULL;
	byte *buf;
	int len;

	len = FS_FOpenFile(path, &f);
	if (len < 0 || !f) {
		if (buffer)
			*buffer = NULL;
		return -1;
	}

	if (!buffer) {
		FS_FCloseFile(f);
		return len;
	}

	buf = (byte *)Z_TagMalloc(len, tag);
	*buffer = buf;
	if (!buf) {
		FS_FCloseFile(f);
		return -1;
	}
	FS_Read(buf, len, f);
	FS_FCloseFile(f);
	return len;
}

//==============================================

/*
===============
SV_ShutdownGameProgs

Called when either the entire server is being killed, or
it is changing to a different game directory.
===============
*/
void SV_ShutdownGameProgs (void)
{
	if (!ge)
		return;
	ge->Shutdown ();
	Sys_UnloadGame ();
	ge = NULL;

	DebugLines = NULL;
	numDebugLines = 0;
}

/*
===============
SV_InitGameProgs

Init the game subsystem for a new map
===============
*/
void SCR_DebugGraph (float value, int color);

void SV_InitGameProgs (void)
{
	game_import_t	import;

	// unload anything we have now
	if (ge)
	{
		SV_ShutdownGameProgs ();
	}

	DebugLines = NULL;
	numDebugLines = 0;

	// initialize sinmdl handles
	memset (SINMDL_handles, 0, sizeof(SINMDL_handles));

	// load a new game dll
	import.multicast = SV_Multicast;
	import.unicast = PF_Unicast;
	import.bprintf = SV_BroadcastPrintf;
	import.dprintf = PF_dprintf;
	import.printf = PF_printf;
	import.cprintf = PF_cprintf;
	import.centerprintf = PF_centerprintf;
	import.error = PF_error;

	import.linkentity = SV_LinkEdict;
	import.unlinkentity = SV_UnlinkEdict;
	import.BoxEdicts = SV_AreaEdicts;
	import.trace = SV_Trace;
	import.fulltrace = SV_FullTrace;
	import.pointcontents = SV_PointContents;
	import.setmodel = PF_setmodel;
	import.inPVS = PF_inPVS;
	import.inPHS = PF_inPHS;
	import.Pmove = Pmove;

	import.modelindex = PF_modelindex;
	import.itemindex  = SV_ItemIndex;
	import.soundindex = SV_SoundIndex;
	import.imageindex = SV_ImageIndex;

	import.configstring = PF_Configstring;
	import.sound = PF_StartSound;
	import.positioned_sound = SV_StartSound;

	import.WriteChar = PF_WriteChar;
	import.WriteByte = PF_WriteByte;
	import.WriteShort = PF_WriteShort;
	import.WriteLong = PF_WriteLong;
	import.WriteFloat = PF_WriteFloat;
	import.WriteString = PF_WriteString;
	import.WritePosition = PF_WritePos;
	import.WriteDir = PF_WriteDir;
	import.WriteAngle = PF_WriteAngle;

	import.TagMalloc = Z_TagMalloc;
	import.TagFree = Z_Free;
	import.FreeTags = Z_FreeTags;

	import.cvar = Cvar_Get;
	import.cvar_set = Cvar_Set;
	import.cvar_forceset = Cvar_ForceSet;

	import.argc = Cmd_Argc;
	import.argv = Cmd_Argv;
	import.args = Cmd_Args;
	import.AddCommandString = Cbuf_AddText;

	import.DebugGraph = SCR_DebugGraph;
	import.SetAreaPortalState = CM_SetAreaPortalState;
	import.AreasConnected = CM_AreasConnected;

	import.LoadFile = PF_LoadFile;
	import.GameDir = FS_Gamedir;
	import.PlayerDir = FS_Playerdir;
	import.CreatePath = FS_CreatePath;
	import.SoundLength = SoundTimeLength;

	import.IsModel = PF_IsModel;
	import.NumAnims = PF_NumAnims;
	import.NumSkins = PF_NumSkins;
	import.NumGroups = PF_NumGroups;
	import.InitCommands = PF_InitCommands;
	import.CalculateBounds = PF_CalculateBounds;
	import.Anim_NameForNum = PF_Anim_NameForNum;
	import.Anim_NumForName = PF_Anim_NumForName;
	import.Anim_Random = PF_Anim_Random;
	import.Anim_NumFrames = PF_Anim_NumFrames;
	import.Anim_Time = PF_Anim_Time;
	import.Anim_Delta = PF_Anim_Delta;
	import.Frame_Commands = PF_Frame_Commands;
	import.Frame_Delta = PF_Frame_Delta;
	import.Frame_Time = PF_Frame_Time;
	import.Skin_NameForNum = PF_Skin_NameForNum;
	import.Skin_NumForName = PF_Skin_NumForName;
	import.Group_NameToNum = PF_Group_NumForName;
	import.Group_NumToName = PF_Group_NameForNum;
	import.Group_DamageMultiplier = PF_Group_DamageMultiplier;
	import.Group_Flags = PF_GroupFlags;
	import.GetBoneInfo = PF_GetBoneInfo;
	import.GetBoneGroupName = PF_GetBoneGroupName;
	import.GetBoneTransform = PF_GetBoneTransform;
	import.Alias_Add = PF_Alias_Add;
	import.Alias_FindRandom = PF_Alias_FindRandom;
	import.Alias_Dump = PF_Alias_Dump;
	import.Alias_Clear = PF_Alias_Clear;
	//
	// Alias stuff
	//
	import.GlobalAlias_Add = Alias_Add;
	import.GlobalAlias_FindRandom = Alias_FindRandom;
	import.GlobalAlias_Dump = Alias_Dump;
	import.GlobalAlias_Clear = Alias_Clear;

	import.DebugLines		= &DebugLines;
	import.numDebugLines	= &numDebugLines;

	import.IncrementStatusCount = Com_IncrementStatusCount;
	import.Surf_NumSurfaces     = CM_NumSurfinfos;
	import.Surf_Surfaces        = CM_Surfinfos;

	import.CalcCRC = CRC_Block;

	ge = (game_export_t *)Sys_GetGameAPI(&import);

	if (!ge)
	{
		Com_Error (ERR_DROP, "failed to load game DLL");
	}
	if (ge->apiversion != GAME_API_VERSION)
	{
		Com_Error (ERR_DROP, "game is version %i, not %i", ge->apiversion,
		GAME_API_VERSION);
	}
	if (ge) {
		ge->Init();
	}
}
