// vi: set ts=4 sw=4 :
// vim: set tw=75 :

// metamod.h - (main) description of metamod operations

/*
 * Copyright (c) 2001-2003 Will Day <willday@hpgx.net>
 *
 *    This file is part of Metamod.
 *
 *    Metamod is free software; you can redistribute it and/or modify it
 *    under the terms of the GNU General Public License as published by the
 *    Free Software Foundation; either version 2 of the License, or (at
 *    your option) any later version.
 *
 *    Metamod is distributed in the hope that it will be useful, but
 *    WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with Metamod; if not, write to the Free Software Foundation,
 *    Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *    In addition, as a special exception, the author gives permission to
 *    link the code of this program with the Half-Life Game Engine ("HL
 *    Engine") and Modified Game Libraries ("MODs") developed by Valve,
 *    L.L.C ("Valve").  You must obey the GNU General Public License in all
 *    respects for all of the code used other than the HL Engine and MODs
 *    from Valve.  If you modify this file, you may extend this exception
 *    to your version of the file, but you are not obligated to do so.  If
 *    you do not wish to do so, delete this exception statement from your
 *    version.
 *
 */

#ifndef METAMOD_H
#define METAMOD_H

#include "meta_api.h"			// META_RES, etc
#include "mlist.h"				// MPluginList, etc
#include "mreg.h"				// MRegCmdList, etc
#include "conf_meta.h"			// MConfig
#include "osdep.h"				// NAME_MAX, etc
#include "types_meta.h"			// mBOOL

// file that lists plugins to load at startup
#define PLUGINS_INI			"addons/metamod/plugins.ini"
#define OLD_PLUGINS_INI		"metamod.ini"

// file that contains commands to metamod plugins at startup
#define EXEC_CFG			"addons/metamod/exec.cfg"
#define OLD_EXEC_CFG		"metaexec.cfg"

// previously, file that contained path for an override-gamedll
#define OLD_GAMEDLL_TXT		"metagame.ini"

// generic config file
#define CONFIG_INI			"addons/metamod/config.ini"

// cvar to contain version
extern cvar_t meta_version;

// Info about the game dll/mod.
typedef struct gamedll_s {
	char name[NAME_MAX];		// ie "cstrike" (from gamedir)
	char *desc;					// ie "Counter-Strike"
	char gamedir[PATH_MAX];		// ie "/home/willday/half-life/cstrike"
	char pathname[PATH_MAX];	// ie "/home/willday/half-life/cstrike/dlls/cs_i386.so"
	char const *file;			// ie "cs_i386.so"
	char real_pathname[PATH_MAX];	// in case pathname overridden by bot, etc
	DLHANDLE handle;
	gamedll_funcs_t funcs;		// dllapi_table, newapi_table
} gamedll_t;
extern gamedll_t GameDLL;

// SDK variables for storing engine funcs and globals.
extern enginefuncs_t g_engfuncs;
extern globalvars_t  *gpGlobals;

// Our modified version of the engine funcs, to give to plugins.
extern enginefuncs_t g_plugin_engfuncs;

// Our structure for storing engine references.
typedef struct engine_s {
	enginefuncs_t	*funcs;			// engine funcs
	globalvars_t	*globals;		// engine globals
	enginefuncs_t	*pl_funcs;		// "modified" eng funcs we give to plugins
} engine_t;
extern engine_t Engine;

// Config structure.
extern MConfig *Config;

// List of plugins loaded/opened/running.
extern MPluginList *Plugins;

// List of command functions registered by plugins.
extern MRegCmdList *RegCmds;

// List of cvar structures registered by plugins.
extern MRegCvarList *RegCvars;

// List of user messages registered by gamedll.
extern MRegMsgList *RegMsgs;

#ifdef UNFINISHED
// List of event/logline hooks requested by plugins.
extern MHookList *Hooks;
#endif /* UNFINISHED */

// Data provided to plugins.
// Separate copies to prevent plugins from modifying "readable" parts.
// See meta_api.h for meta_globals_t structure.
extern meta_globals_t PublicMetaGlobals;
extern meta_globals_t PrivateMetaGlobals;

void metamod_startup(void);

mBOOL meta_init_gamedll(void);
mBOOL meta_load_gamedll(void);

// ===== lotsa macros... ======================================================

// These are the meat of the metamod processing, and are as ugly as (or
// uglier) than they look.  This is done via macros, because of the varying
// parameter types (int, void, edict_t*, etc) as well as varying
// function-pointer types and different api tables (dllapi, newapi,
// engine), which just can't be passed to a function.  And, since the
// operation is similar for each api call, I didn't want to keep
// duplicating code all over the place.  Thus the ugly macros.
//
// The basic operation is, for each api call:
//  - iterate through list of plugins
//  - for each plugin, if it provides this api call, then call the
//    function in the plugin
//  - call the "real" function (in the game dll, or from the engine)
//  - for each plugin, check for a "post" version of the function, and call
//    if present
//
//
// Also, for any api call, each plugin has the opportunity to replace the
// real routine, in two ways:
//  - prevent the real routine from being called ("supercede")
//  - allow the real routine to be called, but change the value that's
//    returned ("override")
//
// Thus after each plugin is called, its META_RETURN flag is checked, and
// action taken appropriately.  Note that supercede/override only affects
// the _real_ routine; other plugins will still be called.
//
// In addition to the SUPERCEDE and OVERRIDE flags, there are two
// additional flags a plugin can return:
//  - HANDLED ("I did something here")
//  - IGNORED ("I didn't really do anything")
//
// These aren't used by metamod itself, but could be used by plugins to
// get an idea if a previous plugin did anything.
//
//
// The 5 basic macros are:
//   SETUP
//   CALL_PLUGIN
//   CALL_GAME  and  CALL_ENGINE
//   RETURN
//
// These 5 are actually used to build second level macros for each api type
// (dllapi, newapi, engine), with the CALL_PLUGIN macro being used twice
// (before and after).  Altogether, they end up expanding to approx 150
// lines of code for _each_ api call.  Ack, ugly indeed.
//
// However, due to some functions returning 'void', and others returning an
// actual value, I had to have separate macros for the two, since I
// couldn't seem to generalize the two into a form that the compiler would
// accept.  Thus there are "_void" versions of the 5 macros; these are
// listed first.

// ===== macros for void-returning functions ==================================

// declare/init some variables
#define SETUP_API_CALLS_void(FN_TYPE, pfnName, api_info_table) \
	int i; \
	META_RES mres=MRES_UNSET, status=MRES_UNSET, prev_mres=MRES_UNSET; \
	MPlugin *iplug; \
	FN_TYPE pfn_routine=NULL; \
	int loglevel=api_info_table.pfnName.loglevel; \
	char *pfn_string=api_info_table.pfnName.name; \
	memset(&PublicMetaGlobals, 0, sizeof(PublicMetaGlobals));

// call each plugin
#define CALL_PLUGIN_API_void(post, pfnName, pfn_args, api_table) \
	prev_mres=MRES_UNSET; \
	for(i=0; i < Plugins->endlist; i++) { \
		iplug=&Plugins->plist[i]; \
		if (iplug->status != PL_RUNNING) \
			continue; \
		if(iplug->api_table && (pfn_routine=iplug->api_table->pfnName)); \
		else \
			/* plugin doesn't provide this function */  \
			continue; \
		/* initialize PublicMetaGlobals */ \
		PublicMetaGlobals.mres = MRES_UNSET; \
		PublicMetaGlobals.prev_mres = prev_mres; \
		PublicMetaGlobals.status = status; \
		/* call plugin */ \
		META_DEBUG(loglevel, ("Calling %s:%s%s()", iplug->file, pfn_string, (post?"_Post":""))); \
		pfn_routine pfn_args; \
		/* plugin's result code */ \
		mres=PublicMetaGlobals.mres; \
		if(mres > status) \
			status = mres; \
		/* save this for successive plugins to see */ \
		prev_mres = mres; \
		if(mres==MRES_UNSET) \
			META_ERROR("Plugin didn't set meta_result: %s:%s%s()", iplug->file, pfn_string, (post?"_Post":"")); \
		if(post && mres==MRES_SUPERCEDE) \
			META_ERROR("MRES_SUPERCEDE not valid in Post functions: %s:%s%s()", iplug->file, pfn_string, (post?"_Post":"")); \
	}

// call "real" function, from gamedll
#define CALL_GAME_API_void(pfnName, pfn_args, api_table) \
	if(status==MRES_SUPERCEDE) { \
		META_DEBUG(loglevel, ("Skipped (supercede) %s:%s()", GameDLL.file, pfn_string)); \
		/* don't return here; superceded game routine, but still allow \
		 * _post routines to run. \
		 */ \
	} \
	else if(GameDLL.funcs.api_table) { \
		pfn_routine=NULL; \
		if(GameDLL.funcs.api_table && (pfn_routine=GameDLL.funcs.api_table->pfnName)); \
		if(pfn_routine) { \
			META_DEBUG(loglevel, ("Calling %s:%s()", GameDLL.file, pfn_string)); \
			pfn_routine pfn_args; \
		} \
		/* don't complain for NULL routines in NEW_DLL_FUNCTIONS  */ \
		else if((void*) GameDLL.funcs.api_table != (void*) GameDLL.funcs.newapi_table) { \
			META_ERROR("Couldn't find api call: %s:%s", GameDLL.file, pfn_string); \
			status=MRES_UNSET; \
		} \
	} \
	else { \
		META_DEBUG(loglevel, ("No api table defined for api call: %s:%s", GameDLL.file, pfn_string)); \
	}

// call "real" function, from engine
#define CALL_ENGINE_API_void(pfnName, pfn_args) \
	if(status==MRES_SUPERCEDE) { \
		META_DEBUG(loglevel, ("Skipped (supercede) engine:%s()", pfn_string)); \
		/* don't return here; superceded game routine, but still allow \
		 * _post routines to run. \
		 */ \
	} \
	else { \
		pfn_routine=NULL; \
		pfn_routine=Engine.funcs->pfnName; \
		if(pfn_routine) { \
			META_DEBUG(loglevel, ("Calling engine:%s()", pfn_string)); \
			pfn_routine pfn_args; \
		} \
		else { \
			META_ERROR("Couldn't find api call: engine:%s", pfn_string); \
			status=MRES_UNSET; \
		} \
	}

// return (void)
#define RETURN_API_void() \
	return;


// ===== macros for type-returning functions ==================================

// declare/init some variables
#define SETUP_API_CALLS(ret_t, ret_init, FN_TYPE, pfnName, api_info_table) \
	int i; \
	ret_t dllret=ret_init; \
	ret_t override_ret=ret_init; \
	ret_t pub_override_ret=ret_init; \
	ret_t orig_ret=ret_init; \
	ret_t pub_orig_ret=ret_init; \
	META_RES mres=MRES_UNSET, status=MRES_UNSET, prev_mres=MRES_UNSET; \
	MPlugin *iplug; \
	FN_TYPE pfn_routine=NULL; \
	int loglevel=api_info_table.pfnName.loglevel; \
	char *pfn_string=api_info_table.pfnName.name; \
	memset(&PublicMetaGlobals, 0, sizeof(PublicMetaGlobals));

// call each plugin
#define CALL_PLUGIN_API(post, ret_init, pfnName, pfn_args, MRES_TYPE, api_table) \
	override_ret=ret_init; \
	prev_mres=MRES_UNSET; \
	for(i=0; i < Plugins->endlist; i++) { \
		if (Plugins->plist[i].status != PL_RUNNING) \
			continue; \
		iplug=&Plugins->plist[i]; \
		if(iplug->api_table && (pfn_routine=iplug->api_table->pfnName)); \
		else \
			/* plugin doesn't provide this function */  \
			continue; \
		/* initialize PublicMetaGlobals */ \
		PublicMetaGlobals.mres = MRES_UNSET; \
		PublicMetaGlobals.prev_mres = prev_mres; \
		PublicMetaGlobals.status = status; \
		pub_orig_ret = orig_ret; \
		PublicMetaGlobals.orig_ret = &pub_orig_ret; \
		if(status==MRES_TYPE) { \
			pub_override_ret = override_ret; \
			PublicMetaGlobals.override_ret = &pub_override_ret; \
		} \
		/* call plugin */ \
		META_DEBUG(loglevel, ("Calling %s:%s%s()", iplug->file, pfn_string, (post?"_Post":""))); \
		dllret=pfn_routine pfn_args; \
		/* plugin's result code */ \
		mres=PublicMetaGlobals.mres; \
		if(mres > status) \
			status = mres; \
		/* save this for successive plugins to see */ \
		prev_mres = mres; \
		if(mres==MRES_TYPE) \
			override_ret = pub_override_ret = dllret; \
		else if(mres==MRES_UNSET) \
			META_ERROR("Plugin didn't set meta_result: %s:%s%s()", iplug->file, pfn_string, (post?"_Post":"")); \
		else if(post && mres==MRES_SUPERCEDE) \
			META_ERROR("MRES_SUPERCEDE not valid in Post functions: %s:%s%s()", iplug->file, pfn_string, (post?"_Post":"")); \
	}

// call "real" function, from gamedll
#define CALL_GAME_API(pfnName, pfn_args, api_table) \
	if(status==MRES_SUPERCEDE) { \
		META_DEBUG(loglevel, ("Skipped (supercede) %s:%s()", GameDLL.file, pfn_string)); \
		orig_ret = pub_orig_ret = override_ret; \
		PublicMetaGlobals.orig_ret = &pub_orig_ret; \
		/* don't return here; superceded game routine, but still allow \
		 * _post routines to run. \
		 */ \
	} \
	else if(GameDLL.funcs.api_table) { \
		pfn_routine=NULL; \
		if(GameDLL.funcs.api_table && (pfn_routine=GameDLL.funcs.api_table->pfnName)); \
		if(pfn_routine) { \
			META_DEBUG(loglevel, ("Calling %s:%s()", GameDLL.file, pfn_string)); \
			dllret=pfn_routine pfn_args; \
			orig_ret = dllret; \
		} \
		/* don't complain for NULL routines in NEW_DLL_FUNCTIONS  */ \
		else if((void*) GameDLL.funcs.api_table != (void*) GameDLL.funcs.newapi_table) { \
			META_ERROR("Couldn't find api call: %s:%s", GameDLL.file, pfn_string); \
			status=MRES_UNSET; \
		} \
	} \
	else { \
		META_DEBUG(loglevel, ("No api table defined for api call: %s:%s", GameDLL.file, pfn_string)); \
	}

// call "real" function, from engine
#define CALL_ENGINE_API(pfnName, pfn_args) \
	if(status==MRES_SUPERCEDE) { \
		META_DEBUG(loglevel, ("Skipped (supercede) engine:%s()", pfn_string)); \
		orig_ret = pub_orig_ret = override_ret; \
		PublicMetaGlobals.orig_ret = &pub_orig_ret; \
		/* don't return here; superceded game routine, but still allow \
		 * _post routines to run. \
		 */ \
	} \
	else { \
		pfn_routine=NULL; \
		pfn_routine=Engine.funcs->pfnName; \
		if(pfn_routine) { \
			META_DEBUG(loglevel, ("Calling engine:%s()", pfn_string)); \
			dllret=pfn_routine pfn_args; \
			orig_ret = dllret; \
		} \
		else { \
			META_ERROR("Couldn't find api call: engine:%s", pfn_string); \
			status=MRES_UNSET; \
		} \
	}

// return a value
#define RETURN_API() \
	if(status==MRES_OVERRIDE) { \
		META_DEBUG(loglevel, ("Returning (override) %s()", pfn_string)); \
		return(override_ret); \
	} \
	else \
		return(orig_ret);

// ===== end macros ===========================================================


#endif /* METAMOD_H */
