Skip to content

Commit

Permalink
Added the ability to hook exposed functions
Browse files Browse the repository at this point in the history
This requires that the function info has been added!
  • Loading branch information
Ayuto committed Jun 3, 2015
1 parent 8798d8d commit 408e95b
Show file tree
Hide file tree
Showing 7 changed files with 715 additions and 11 deletions.
9 changes: 4 additions & 5 deletions addons/source-python/packages/source-python/memory/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
# Memory
from _memory import HookType
from memory import Function
from memory import get_function_info


# =============================================================================
Expand All @@ -33,11 +34,9 @@ def __init__(self, function):
"""Verify the given function is a Function object and store it."""
# Is the function to be hooked a Function instance?
if not isinstance(function, Function):

# Raise an error as we can only hook Function instances
raise TypeError(
"'" + type(function).__name__ +
"' object is not a Function instance.")
# Try getting the function info
info = get_function_info(function)
function = info.make_function(function.__self__)

# Store the function
self.function = function
Expand Down
10 changes: 7 additions & 3 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,9 @@ Set(SOURCEPYTHON_MEMORY_MODULE_HEADERS
core/modules/memory/memory_tools.h
core/modules/memory/memory_scanner.h
core/modules/memory/memory_hooks.h
core/modules/memory/memory_calling_convention.h
core/modules/memory/memory_function_info.h
core/modules/memory/memory_utilities.h
)

Set(SOURCEPYTHON_MEMORY_MODULE_SOURCES
Expand Down Expand Up @@ -366,6 +369,10 @@ Set(SOURCEPYTHON_WEAPONS_MODULE_SOURCES
# All module source files
# ------------------------------------------------------------------
Set(SOURCEPYTHON_MODULE_FILES
# CFunctionInfo must be exposed at first
${SOURCEPYTHON_MEMORY_MODULE_HEADERS}
${SOURCEPYTHON_MEMORY_MODULE_SOURCES}

${SOURCEPYTHON_BITBUFFERS_MODULE_HEADERS}
${SOURCEPYTHON_BITBUFFERS_MODULE_SOURCES}

Expand Down Expand Up @@ -407,9 +414,6 @@ Set(SOURCEPYTHON_MODULE_FILES
${SOURCEPYTHON_MATHLIB_MODULE_HEADERS}
${SOURCEPYTHON_MATHLIB_MODULE_SOURCES}

${SOURCEPYTHON_MEMORY_MODULE_HEADERS}
${SOURCEPYTHON_MEMORY_MODULE_SOURCES}

${SOURCEPYTHON_MESSAGES_MODULE_HEADERS}
${SOURCEPYTHON_MESSAGES_MODULE_SOURCES}
${SOURCEPYTHON_MESSAGES_MODULE_GAME_SOURCES}
Expand Down
7 changes: 5 additions & 2 deletions src/core/modules/events/events_wrap_python.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,10 +169,12 @@ void export_igameeventlistener(scope _events)
// Exports IGameEventManager2.
//-----------------------------------------------------------------------------
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(create_event_overload, CreateEvent, 2, 2);

#include "modules/memory/memory_utilities.h"
void export_igameeventmanager(scope _events)
{
class_<IGameEventManager2, boost::noncopyable>("_GameEventManager", no_init)
class_<IGameEventManager2, boost::noncopyable> _GameEventManager("_GameEventManager", no_init);

_GameEventManager
.def("load_events_from_file",
&IGameEventManager2::LoadEventsFromFile,
"Loads game event descriptions from a file eg resource/gameevents.res",
Expand Down Expand Up @@ -244,6 +246,7 @@ void export_igameeventmanager(scope _events)

ADD_MEM_TOOLS(IGameEventManager2)
;
AddGetFunctionInfo(_GameEventManager, "fire_event", &IGameEventManager2::FireEvent);

_events.attr("game_event_manager") = object(ptr(gameeventmanager));
}
112 changes: 112 additions & 0 deletions src/core/modules/memory/memory_calling_convention.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/**
* =============================================================================
* Source Python
* Copyright (C) 2015 Source Python Development Team. All rights reserved.
* =============================================================================
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 3.0, as published by the
* Free Software Foundation.
*
* This program 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
* this program. If not, see <http://www.gnu.org/licenses/>.
*
* As a special exception, the Source Python Team gives you permission
* to link the code of this program (as well as its derivative works) to
* "Half-Life 2," the "Source Engine," and any Game MODs that run on software
* by the Valve Corporation. You must obey the GNU General Public License in
* all respects for all other code used. Additionally, the Source.Python
* Development Team grants this exception to all derivative works.
*/

// The following code is taken from here. Thank you!
// http://members.gamedev.net/sicrane/articles/metaprogramming.html
// It has been altered, so that it correctly detects calling
// conventions and works on Linux as well.

#if !BOOST_PP_IS_ITERATING
#ifndef _MEMORY_CALLING_CONVENTION_H
#define _MEMORY_CALLING_CONVENTION_H

#include "modules/memory/memory_tools.h"
#include <boost/type_traits.hpp>

#include <boost/preprocessor/iteration/iterate.hpp>

#include <boost/preprocessor/repetition/enum_params.hpp>
#include <boost/preprocessor/repetition/enum_trailing_params.hpp>

#ifdef _WIN32
#define CC_CDECL __cdecl
#elif defined(__linux__)
#define CC_CDECL
#else
#error "Unsupported platform."
#endif

class Undefined;
void TypeDoesntSeemToBeAFunction(Undefined &);

template <typename T>
struct CallingConvention {
CallingConvention() {
TypeDoesntSeemToBeAFunction(T());
}
};

// Call this function to get the calling convention of the given function
template <typename Function>
Convention_t GetCallingConvention(Function func) {
return CallingConvention<Function>();
}

#define BOOST_PP_ITERATION_PARAMS_1 (3, (0, BOOST_PYTHON_MAX_ARITY, "modules/memory/memory_calling_convention.h"))
#include BOOST_PP_ITERATE()

#endif // _MEMORY_CALLING_CONVENTION_H
#elif BOOST_PP_ITERATION_DEPTH() == 1
#define NUM_ARGS BOOST_PP_FRAME_ITERATION(1)

// Handle function of the global scope
template<typename R BOOST_PP_ENUM_TRAILING_PARAMS(NUM_ARGS, typename A)>
struct CallingConvention<R (CC_CDECL *)(BOOST_PP_ENUM_PARAMS(NUM_ARGS, A))>
: boost::integral_constant<Convention_t, CONV_CDECL> {};

// Handle variadic functions
template<typename R BOOST_PP_ENUM_TRAILING_PARAMS(NUM_ARGS, typename A)>
struct CallingConvention<R (CC_CDECL *)(BOOST_PP_ENUM_PARAMS(NUM_ARGS, A) ...)>
: boost::integral_constant<Convention_t, CONV_CDECL> {};

#ifdef _WIN32
// We only need to care about __stdcall on Windows
template<typename R BOOST_PP_ENUM_TRAILING_PARAMS(NUM_ARGS, typename A)>
struct CallingConvention<R (__stdcall *)(BOOST_PP_ENUM_PARAMS(NUM_ARGS, A))>
: boost::integral_constant<Convention_t, CONV_STDCALL> {};
#endif // _WIN32

// Handle class methods
template<typename T, typename R BOOST_PP_ENUM_TRAILING_PARAMS(NUM_ARGS, typename A)>
struct CallingConvention<R (CC_CDECL T::*)(BOOST_PP_ENUM_PARAMS(NUM_ARGS, A))>
: boost::integral_constant<Convention_t, CONV_CDECL> {};

// Handle variadic class methods
template<typename T, typename R BOOST_PP_ENUM_TRAILING_PARAMS(NUM_ARGS, typename A)>
struct CallingConvention<R (CC_CDECL T::*)(BOOST_PP_ENUM_PARAMS(NUM_ARGS, A) ...)>
: boost::integral_constant<Convention_t, CONV_CDECL> {};

#ifdef _WIN32
// We only need to care about __thiscall and __stdcall on Windows
template<typename T, typename R BOOST_PP_ENUM_TRAILING_PARAMS(NUM_ARGS, typename A)>
struct CallingConvention<R (__thiscall T::*)(BOOST_PP_ENUM_PARAMS(NUM_ARGS, A))>
: boost::integral_constant<Convention_t, CONV_THISCALL> {};

template<typename T, typename R BOOST_PP_ENUM_TRAILING_PARAMS(NUM_ARGS, typename A)>
struct CallingConvention<R (__stdcall T::*)(BOOST_PP_ENUM_PARAMS(NUM_ARGS, A))>
: boost::integral_constant<Convention_t, CONV_STDCALL> {};
#endif // _WIN32
#endif // BOOST_PP_IS_ITERATING

1 comment on commit 408e95b

@Ayuto
Copy link
Member Author

@Ayuto Ayuto commented on 408e95b Jun 3, 2015

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Example:

import memory

from memory.hooks import PreHook

from events import GameEvent
from events.manager import game_event_manager

@PreHook(game_event_manager.fire_event)
def pre_fire_event(args):
    event = memory.make_object(GameEvent, args[1])
    print('Event fired:', event.get_name())

Please sign in to comment.