1.1 --- a/extensions/sdktools/output.cpp Fri Jul 11 06:51:21 2008 +0000
1.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
1.3 @@ -1,489 +0,0 @@
1.4 -/**
1.5 - * vim: set ts=4 :
1.6 - * =============================================================================
1.7 - * SourceMod SDKTools Extension
1.8 - * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
1.9 - * =============================================================================
1.10 - *
1.11 - * This program is free software; you can redistribute it and/or modify it under
1.12 - * the terms of the GNU General Public License, version 3.0, as published by the
1.13 - * Free Software Foundation.
1.14 - *
1.15 - * This program is distributed in the hope that it will be useful, but WITHOUT
1.16 - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
1.17 - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
1.18 - * details.
1.19 - *
1.20 - * You should have received a copy of the GNU General Public License along with
1.21 - * this program. If not, see <http://www.gnu.org/licenses/>.
1.22 - *
1.23 - * As a special exception, AlliedModders LLC gives you permission to link the
1.24 - * code of this program (as well as its derivative works) to "Half-Life 2," the
1.25 - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
1.26 - * by the Valve Corporation. You must obey the GNU General Public License in
1.27 - * all respects for all other code used. Additionally, AlliedModders LLC grants
1.28 - * this exception to all derivative works. AlliedModders LLC defines further
1.29 - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
1.30 - * or <http://www.sourcemod.net/license.php>.
1.31 - *
1.32 - * Version: $Id$
1.33 - */
1.34 -
1.35 -#include "extension.h"
1.36 -#include "output.h"
1.37 -
1.38 -ISourcePawnEngine *spengine = NULL;
1.39 -EntityOutputManager g_OutputManager;
1.40 -
1.41 -EntityOutputManager::EntityOutputManager()
1.42 -{
1.43 - info_address = NULL;
1.44 - info_callback = NULL;
1.45 - HookCount = 0;
1.46 - is_detoured = false;
1.47 - enabled = false;
1.48 -}
1.49 -
1.50 -EntityOutputManager::~EntityOutputManager()
1.51 -{
1.52 - if (!enabled)
1.53 - {
1.54 - return;
1.55 - }
1.56 -
1.57 - EntityOutputs->Destroy();
1.58 - ClassNames->Destroy();
1.59 - ShutdownFireEventDetour();
1.60 -}
1.61 -
1.62 -void EntityOutputManager::Init()
1.63 -{
1.64 - enabled = CreateFireEventDetour();
1.65 -
1.66 - if (!enabled)
1.67 - {
1.68 - return;
1.69 - }
1.70 -
1.71 - EntityOutputs = adtfactory->CreateBasicTrie();
1.72 - ClassNames = adtfactory->CreateBasicTrie();
1.73 -}
1.74 -
1.75 -bool EntityOutputManager::IsEnabled()
1.76 -{
1.77 - return enabled;
1.78 -}
1.79 -
1.80 -bool EntityOutputManager::CreateFireEventDetour()
1.81 -{
1.82 - if (!g_pGameConf->GetMemSig("FireOutput", &info_address))
1.83 - {
1.84 - return false;
1.85 - }
1.86 -
1.87 - if (!info_address)
1.88 - {
1.89 - g_pSM->LogError(myself, "Could not locate FireOutput - Disabling Entity Outputs");
1.90 - return false;
1.91 - }
1.92 -
1.93 - if (!g_pGameConf->GetOffset("FireOutputBackup", (int *)&(info_restore.bytes)))
1.94 - {
1.95 - return false;
1.96 - }
1.97 -
1.98 - /* First, save restore bits */
1.99 - for (size_t i=0; i<info_restore.bytes; i++)
1.100 - {
1.101 - info_restore.patch[i] = ((unsigned char *)info_address)[i];
1.102 - }
1.103 -
1.104 - info_callback = spengine->ExecAlloc(100);
1.105 - JitWriter wr;
1.106 - JitWriter *jit = ≀
1.107 - wr.outbase = (jitcode_t)info_callback;
1.108 - wr.outptr = wr.outbase;
1.109 -
1.110 - /* Function we are detouring into is
1.111 - *
1.112 - * void FireEventDetour(CBaseEntityOutput(void *) *pOutput, CBaseEntity *pActivator, CBaseEntity *pCaller, float fDelay = 0 )
1.113 - */
1.114 -
1.115 - /* push fDelay [esp+20h]
1.116 - * push pCaller [esp+1Ch]
1.117 - * push pActivator [esp+18h]
1.118 - * push pOutput [ecx]
1.119 - */
1.120 -
1.121 -
1.122 -#if defined PLATFORM_WINDOWS
1.123 -
1.124 - IA32_Push_Rm_Disp8_ESP(jit, 32);
1.125 - IA32_Push_Rm_Disp8_ESP(jit, 32);
1.126 - IA32_Push_Rm_Disp8_ESP(jit, 32);
1.127 -
1.128 - IA32_Push_Reg(jit, REG_ECX);
1.129 -
1.130 -#elif defined PLATFORM_LINUX
1.131 - IA32_Push_Rm_Disp8_ESP(jit, 20);
1.132 - IA32_Push_Rm_Disp8_ESP(jit, 20);
1.133 - IA32_Push_Rm_Disp8_ESP(jit, 20);
1.134 -
1.135 - IA32_Push_Rm_Disp8_ESP(jit, 16);
1.136 -#endif
1.137 -
1.138 - jitoffs_t call = IA32_Call_Imm32(jit, 0);
1.139 - IA32_Write_Jump32_Abs(jit, call, (void *)TempDetour);
1.140 -
1.141 -
1.142 -#if defined PLATFORM_LINUX
1.143 - IA32_Add_Rm_Imm8(jit, REG_ESP, 4, MOD_REG); //add esp, 4
1.144 -#elif defined PLATFORM_WINDOWS
1.145 - IA32_Pop_Reg(jit, REG_ECX);
1.146 -#endif
1.147 -
1.148 - IA32_Add_Rm_Imm8(jit, REG_ESP, 12, MOD_REG); //add esp, 12 (0Ch)
1.149 -
1.150 -
1.151 - /* Patch old bytes in */
1.152 - for (size_t i=0; i<info_restore.bytes; i++)
1.153 - {
1.154 - jit->write_ubyte(info_restore.patch[i]);
1.155 - }
1.156 -
1.157 - /* Return to the original function */
1.158 - call = IA32_Jump_Imm32(jit, 0);
1.159 - IA32_Write_Jump32_Abs(jit, call, (unsigned char *)info_address + info_restore.bytes);
1.160 -
1.161 - return true;
1.162 -}
1.163 -
1.164 -void EntityOutputManager::InitFireEventDetour()
1.165 -{
1.166 - if (!is_detoured)
1.167 - {
1.168 - DoGatePatch((unsigned char *)info_address, &info_callback);
1.169 - is_detoured = true;
1.170 - }
1.171 -}
1.172 -
1.173 -void EntityOutputManager::DeleteFireEventDetour()
1.174 -{
1.175 - if (is_detoured)
1.176 - {
1.177 - ShutdownFireEventDetour();
1.178 - }
1.179 -
1.180 - if (info_callback)
1.181 - {
1.182 - /* Free the gate */
1.183 - spengine->ExecFree(info_callback);
1.184 - info_callback = NULL;
1.185 - }
1.186 -}
1.187 -
1.188 -void TempDetour(void *pOutput, CBaseEntity *pActivator, CBaseEntity *pCaller, float fDelay)
1.189 -{
1.190 - g_OutputManager.FireEventDetour(pOutput, pActivator, pCaller, fDelay);
1.191 -}
1.192 -
1.193 -void EntityOutputManager::ShutdownFireEventDetour()
1.194 -{
1.195 - if (info_callback)
1.196 - {
1.197 - /* Remove the patch */
1.198 - ApplyPatch(info_address, 0, &info_restore, NULL);
1.199 - is_detoured = false;
1.200 - }
1.201 -}
1.202 -
1.203 -void EntityOutputManager::FireEventDetour(void *pOutput, CBaseEntity *pActivator, CBaseEntity *pCaller, float fDelay)
1.204 -{
1.205 - char sOutput[20];
1.206 - Q_snprintf(sOutput, sizeof(sOutput), "%x", pOutput);
1.207 -
1.208 - // attempt to directly lookup a hook using the pOutput pointer
1.209 - OutputNameStruct *pOutputName = NULL;
1.210 -
1.211 - edict_t *pEdict = gameents->BaseEntityToEdict(pCaller);
1.212 -
1.213 - /* TODO: Add support for entities without an edict */
1.214 - if (pEdict == NULL)
1.215 - {
1.216 - return;
1.217 - }
1.218 -
1.219 - bool fastLookup = false;
1.220 -
1.221 - // Fast lookup failed - check the slow way for hooks that havn't fired yet
1.222 - if ((fastLookup = EntityOutputs->Retrieve(sOutput, (void **)&pOutputName)) == false)
1.223 - {
1.224 - const char *classname = pEdict->GetClassName();
1.225 - const char *outputname = FindOutputName(pOutput, pCaller);
1.226 -
1.227 - pOutputName = FindOutputPointer(classname, outputname, false);
1.228 -
1.229 - if (!pOutputName)
1.230 - {
1.231 - return;
1.232 - }
1.233 - }
1.234 -
1.235 - if (!pOutputName->hooks.empty())
1.236 - {
1.237 - if (!fastLookup)
1.238 - {
1.239 - // hook exists on this classname and output - map it into our quick find trie
1.240 - EntityOutputs->Insert(sOutput, pOutputName);
1.241 - }
1.242 -
1.243 - SourceHook::List<omg_hooks *>::iterator _iter;
1.244 -
1.245 - omg_hooks *hook;
1.246 -
1.247 - _iter = pOutputName->hooks.begin();
1.248 -
1.249 - while (_iter != pOutputName->hooks.end())
1.250 - {
1.251 - hook = (omg_hooks *)*_iter;
1.252 -
1.253 - hook->in_use = true;
1.254 -
1.255 - int serial = pEdict->m_NetworkSerialNumber;
1.256 -
1.257 - if (serial != hook->entity_filter && hook->entity_index == engine->IndexOfEdict(pEdict))
1.258 - {
1.259 - // same entity index but different serial number. Entity has changed, kill the hook.
1.260 - _iter = pOutputName->hooks.erase(_iter);
1.261 - CleanUpHook(hook);
1.262 -
1.263 - continue;
1.264 - }
1.265 -
1.266 - if (hook->entity_filter == -1 || hook->entity_filter == serial) // Global classname hook
1.267 - {
1.268 - //fire the forward to hook->pf
1.269 - hook->pf->PushString(pOutputName->Name);
1.270 - hook->pf->PushCell(engine->IndexOfEdict(pEdict));
1.271 -
1.272 - edict_t *pEdictActivator = gameents->BaseEntityToEdict(pActivator);
1.273 - if (!pEdictActivator)
1.274 - {
1.275 - hook->pf->PushCell(-1);
1.276 - }
1.277 - else
1.278 - {
1.279 - hook->pf->PushCell(engine->IndexOfEdict(pEdictActivator));
1.280 - }
1.281 - //hook->pf->PushCell(handle);
1.282 - hook->pf->PushFloat(fDelay);
1.283 - hook->pf->Execute(NULL);
1.284 -
1.285 - if ((hook->entity_filter != -1) && hook->only_once)
1.286 - {
1.287 - _iter = pOutputName->hooks.erase(_iter);
1.288 - CleanUpHook(hook);
1.289 -
1.290 - continue;
1.291 - }
1.292 -
1.293 - if (hook->delete_me)
1.294 - {
1.295 - _iter = pOutputName->hooks.erase(_iter);
1.296 - CleanUpHook(hook);
1.297 - continue;
1.298 - }
1.299 - }
1.300 -
1.301 - hook->in_use = false;
1.302 - _iter++;
1.303 - }
1.304 - }
1.305 -}
1.306 -
1.307 -omg_hooks *EntityOutputManager::NewHook()
1.308 -{
1.309 - omg_hooks *hook;
1.310 -
1.311 - if (FreeHooks.empty())
1.312 - {
1.313 - hook = new omg_hooks;
1.314 - }
1.315 - else
1.316 - {
1.317 - hook = g_OutputManager.FreeHooks.front();
1.318 - g_OutputManager.FreeHooks.pop();
1.319 - }
1.320 -
1.321 - return hook;
1.322 -}
1.323 -
1.324 -void EntityOutputManager::OnHookAdded()
1.325 -{
1.326 - HookCount++;
1.327 -
1.328 - if (HookCount == 1)
1.329 - {
1.330 - // This is the first hook created
1.331 - InitFireEventDetour();
1.332 - }
1.333 -}
1.334 -
1.335 -void EntityOutputManager::OnHookRemoved()
1.336 -{
1.337 - HookCount--;
1.338 -
1.339 - if (HookCount == 0)
1.340 - {
1.341 - ShutdownFireEventDetour();
1.342 - }
1.343 -}
1.344 -
1.345 -void EntityOutputManager::CleanUpHook(omg_hooks *hook)
1.346 -{
1.347 - FreeHooks.push(hook);
1.348 -
1.349 - OnHookRemoved();
1.350 -
1.351 - IPlugin *pPlugin = plsys->FindPluginByContext(hook->pf->GetParentContext()->GetContext());
1.352 - SourceHook::List<omg_hooks *> *pList = NULL;
1.353 -
1.354 - if (!pPlugin->GetProperty("OutputHookList", (void **)&pList, false) || !pList)
1.355 - {
1.356 - return;
1.357 - }
1.358 -
1.359 - SourceHook::List<omg_hooks *>::iterator p_iter = pList->begin();
1.360 -
1.361 - omg_hooks *pluginHook;
1.362 -
1.363 - while (p_iter != pList->end())
1.364 - {
1.365 - pluginHook = (omg_hooks *)*p_iter;
1.366 - if (pluginHook == hook)
1.367 - {
1.368 - p_iter = pList->erase(p_iter);
1.369 - }
1.370 - else
1.371 - {
1.372 - p_iter++;
1.373 - }
1.374 - }
1.375 -}
1.376 -
1.377 -void EntityOutputManager::OnPluginDestroyed(IPlugin *plugin)
1.378 -{
1.379 - SourceHook::List<omg_hooks *> *pList = NULL;
1.380 -
1.381 - if (plugin->GetProperty("OutputHookList", (void **)&pList, true))
1.382 - {
1.383 - SourceHook::List<omg_hooks *>::iterator p_iter = pList->begin();
1.384 - omg_hooks *hook;
1.385 -
1.386 - while (p_iter != pList->end())
1.387 - {
1.388 - hook = (omg_hooks *)*p_iter;
1.389 -
1.390 - p_iter = pList->erase(p_iter); //remove from this plugins list
1.391 - hook->m_parent->hooks.remove(hook); // remove from the y's list
1.392 -
1.393 - FreeHooks.push(hook); //save the omg_hook
1.394 -
1.395 - OnHookRemoved();
1.396 - }
1.397 - }
1.398 -}
1.399 -
1.400 -OutputNameStruct *EntityOutputManager::FindOutputPointer(const char *classname, const char *outputname, bool create)
1.401 -{
1.402 - ClassNameStruct *pClassname;
1.403 -
1.404 - if (!ClassNames->Retrieve(classname, (void **)&pClassname))
1.405 - {
1.406 - if (create)
1.407 - {
1.408 - pClassname = new ClassNameStruct;
1.409 - ClassNames->Insert(classname, pClassname);
1.410 - }
1.411 - else
1.412 - {
1.413 - return NULL;
1.414 - }
1.415 - }
1.416 -
1.417 - OutputNameStruct *pOutputName;
1.418 -
1.419 - if (!pClassname->OutputList->Retrieve(outputname, (void **)&pOutputName))
1.420 - {
1.421 - if (create)
1.422 - {
1.423 - pOutputName = new OutputNameStruct;
1.424 - pClassname->OutputList->Insert(outputname, pOutputName);
1.425 - strncpy(pOutputName->Name, outputname, sizeof(pOutputName->Name));
1.426 - pOutputName->Name[49] = 0;
1.427 - }
1.428 - else
1.429 - {
1.430 - return NULL;
1.431 - }
1.432 - }
1.433 -
1.434 - return pOutputName;
1.435 -}
1.436 -
1.437 -// Iterate the datamap of pCaller and look for output pointers with the same address as pOutput
1.438 -const char *EntityOutputManager::FindOutputName(void *pOutput, CBaseEntity *pCaller)
1.439 -{
1.440 - datamap_t *pMap = gamehelpers->GetDataMap(pCaller);
1.441 -
1.442 - while (pMap)
1.443 - {
1.444 - for (int i=0; i<pMap->dataNumFields; i++)
1.445 - {
1.446 - if (pMap->dataDesc[i].flags & FTYPEDESC_OUTPUT)
1.447 - {
1.448 - if ((char *)pCaller + pMap->dataDesc[i].fieldOffset[0] == pOutput)
1.449 - {
1.450 - return pMap->dataDesc[i].externalName;
1.451 - }
1.452 - }
1.453 - }
1.454 - pMap = pMap->baseMap;
1.455 - }
1.456 -
1.457 - return NULL;
1.458 -}
1.459 -
1.460 -// Thanks SM core
1.461 -edict_t *EntityOutputManager::BaseHandleToEdict(CBaseHandle &hndl)
1.462 -{
1.463 - if (!hndl.IsValid())
1.464 - {
1.465 - return NULL;
1.466 - }
1.467 -
1.468 - int index = hndl.GetEntryIndex();
1.469 -
1.470 - edict_t *pStoredEdict;
1.471 -
1.472 - pStoredEdict = engine->PEntityOfEntIndex(index);
1.473 -
1.474 - if (pStoredEdict == NULL)
1.475 - {
1.476 - return NULL;
1.477 - }
1.478 -
1.479 - IServerEntity *pSE = pStoredEdict->GetIServerEntity();
1.480 -
1.481 - if (pSE == NULL)
1.482 - {
1.483 - return NULL;
1.484 - }
1.485 -
1.486 - if (pSE->GetRefEHandle() != hndl)
1.487 - {
1.488 - return NULL;
1.489 - }
1.490 -
1.491 - return pStoredEdict;
1.492 -}