Merge pull request #8 from josh33901/f1ssi0n/gamesystem
Add classid convar, gamesystem, log and doghook singleton
This commit is contained in:
commit
8d2e2fe838
52
src/class_id.cc
Normal file
52
src/class_id.cc
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
#include "stdafx.hh"
|
||||||
|
|
||||||
|
#define PLACE_CHECKER
|
||||||
|
|
||||||
|
#include "class_id.hh"
|
||||||
|
|
||||||
|
#include "interface.hh"
|
||||||
|
#include "log.hh"
|
||||||
|
#include "sdk.hh"
|
||||||
|
|
||||||
|
using namespace sdk;
|
||||||
|
using namespace class_id;
|
||||||
|
|
||||||
|
internal_checker::ClassIDChecker *internal_checker::ClassIDChecker::head = nullptr;
|
||||||
|
|
||||||
|
internal_checker::ClassIDChecker::ClassIDChecker(const char *name, const u32 value) : name(name), intended_value(value) {
|
||||||
|
this->name = name;
|
||||||
|
this->intended_value = value;
|
||||||
|
next = head;
|
||||||
|
head = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
static auto find_class_id(const char *name) {
|
||||||
|
for (auto client_class = IFace<Client>()->get_all_classes();
|
||||||
|
client_class != nullptr;
|
||||||
|
client_class = client_class->next)
|
||||||
|
if (strcmp(client_class->network_name, name) == 0) return client_class->class_id;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool internal_checker::ClassIDChecker::check_correct() {
|
||||||
|
auto found_value = find_class_id(name);
|
||||||
|
|
||||||
|
if (found_value == -1) {
|
||||||
|
Log::msg("[ClassID] Unable to find correct value for '%s'", name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found_value != intended_value) {
|
||||||
|
Log::msg("[ClassID] value for %s is wrong (wanted %d, got %d)", name, intended_value, found_value);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void internal_checker::ClassIDChecker::check_all_correct() {
|
||||||
|
for (auto checker = internal_checker::ClassIDChecker::head; checker != nullptr; checker = checker->next) {
|
||||||
|
checker->check_correct();
|
||||||
|
}
|
||||||
|
}
|
50
src/class_id.hh
Normal file
50
src/class_id.hh
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "platform.hh"
|
||||||
|
|
||||||
|
// This file defines helpers and functionality for using and updating classids
|
||||||
|
|
||||||
|
namespace sdk {
|
||||||
|
namespace class_id {
|
||||||
|
|
||||||
|
// See comment blow for #define ID
|
||||||
|
namespace internal_checker {
|
||||||
|
class ClassIDChecker {
|
||||||
|
static ClassIDChecker *head;
|
||||||
|
|
||||||
|
public:
|
||||||
|
const char * name;
|
||||||
|
u32 intended_value;
|
||||||
|
ClassIDChecker *next = nullptr;
|
||||||
|
|
||||||
|
ClassIDChecker(const char *name, const u32 value);
|
||||||
|
|
||||||
|
bool check_correct();
|
||||||
|
static void check_all_correct();
|
||||||
|
};
|
||||||
|
} // namespace internal_checker
|
||||||
|
|
||||||
|
// For debug builds we want to be able to check our classids are correct and issue warnings if they are not correct
|
||||||
|
// So that we can update the value for next time.
|
||||||
|
#if defined(_DEBUG) && defined(PLACE_CHECKER)
|
||||||
|
#define ID(name, value) \
|
||||||
|
enum { name = value }; \
|
||||||
|
namespace internal_checker { \
|
||||||
|
inline auto checker_##name = ClassIDChecker( \
|
||||||
|
#name, \
|
||||||
|
value); \
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#define ID(name, value) \
|
||||||
|
enum { name = value };
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Put ids here
|
||||||
|
ID(CTFPlayer, 246);
|
||||||
|
ID(CTFRevolver, 284);
|
||||||
|
ID(CTFSniperRifle, 305);
|
||||||
|
|
||||||
|
#undef ID
|
||||||
|
|
||||||
|
} // namespace class_id
|
||||||
|
} // namespace sdk
|
334
src/convar.cc
Normal file
334
src/convar.cc
Normal file
@ -0,0 +1,334 @@
|
|||||||
|
#include "stdafx.hh"
|
||||||
|
|
||||||
|
#include "convar.hh"
|
||||||
|
|
||||||
|
#include "interface.hh"
|
||||||
|
#include "log.hh"
|
||||||
|
#include "sdk.hh"
|
||||||
|
|
||||||
|
// implementation of a source convar
|
||||||
|
namespace sdk {
|
||||||
|
|
||||||
|
class IConVar;
|
||||||
|
|
||||||
|
using ChangeCallbackFn = void (*)(IConVar *, const char *, float);
|
||||||
|
|
||||||
|
static auto dll_identifier = -1;
|
||||||
|
|
||||||
|
class ConCommandBase {
|
||||||
|
public:
|
||||||
|
ConCommandBase() : name(nullptr), value_string(nullptr), help_string(nullptr) {}
|
||||||
|
ConCommandBase(const char *name, const char *help_string, u32 flags = 0) {
|
||||||
|
create_base(name, help_string, flags);
|
||||||
|
}
|
||||||
|
virtual ~ConCommandBase() {
|
||||||
|
IFace<Cvar>()->unregister_command(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool is_command() const { return false; }
|
||||||
|
|
||||||
|
virtual bool has_flag(int flag) const { return (flags & flag); }
|
||||||
|
virtual void add_flag(int new_flags) { flags |= new_flags; }
|
||||||
|
|
||||||
|
virtual const char *get_name() const { return name; }
|
||||||
|
virtual const char *get_help_text() const { return help_string; }
|
||||||
|
|
||||||
|
virtual bool is_registered() const { return registered; }
|
||||||
|
|
||||||
|
virtual int get_dll_identifier() const {
|
||||||
|
if (dll_identifier == -1) dll_identifier = IFace<Cvar>()->allocate_dll_identifier();
|
||||||
|
return dll_identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void create_base(const char *name, const char *help_string, int flags = 0) {
|
||||||
|
assert(name);
|
||||||
|
assert(help_string);
|
||||||
|
|
||||||
|
registered = false;
|
||||||
|
|
||||||
|
this->name = name;
|
||||||
|
this->help_string = help_string;
|
||||||
|
this->flags = flags;
|
||||||
|
|
||||||
|
next = head;
|
||||||
|
head = this;
|
||||||
|
|
||||||
|
// We might not have Cvar here (global variables)
|
||||||
|
|
||||||
|
if (auto cvar = IFace<Cvar>()) {
|
||||||
|
IFace<Cvar>()->register_command(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool init() {
|
||||||
|
IFace<Cvar>()->register_command(this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convar vtable items
|
||||||
|
virtual void set_value(const char *value) {
|
||||||
|
assert(parent == this); // Only valid for root convars.
|
||||||
|
|
||||||
|
float old_value = value_float;
|
||||||
|
|
||||||
|
float new_value;
|
||||||
|
if (value == nullptr)
|
||||||
|
new_value = 0.0f;
|
||||||
|
else
|
||||||
|
new_value = (float)atof(value);
|
||||||
|
|
||||||
|
// if we need to clamp this value then swap the original string
|
||||||
|
// out with a temp one
|
||||||
|
auto new_string_value = value;
|
||||||
|
char temp_value[32];
|
||||||
|
if (clamp_value(new_value) == true) {
|
||||||
|
snprintf(temp_value, sizeof(temp_value), "%f", new_value);
|
||||||
|
new_string_value = temp_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Redetermine value
|
||||||
|
value_float = new_value;
|
||||||
|
value_int = static_cast<int>(value_float);
|
||||||
|
|
||||||
|
// TODO: do we need to handle never as string convars??
|
||||||
|
change_string_value(new_string_value, old_value);
|
||||||
|
}
|
||||||
|
virtual void set_value(float new_value) {
|
||||||
|
assert(parent == this);
|
||||||
|
|
||||||
|
clamp_value(new_value);
|
||||||
|
|
||||||
|
auto old_value = value_float;
|
||||||
|
value_float = new_value;
|
||||||
|
value_int = static_cast<int>(new_value);
|
||||||
|
|
||||||
|
char temp_value[32];
|
||||||
|
snprintf(temp_value, sizeof(temp_value), "%f", new_value);
|
||||||
|
change_string_value(temp_value, old_value);
|
||||||
|
}
|
||||||
|
virtual void set_value(int new_value) {
|
||||||
|
return set_value(static_cast<float>(new_value));
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void internal_set_value(const char *new_value) {
|
||||||
|
return set_value(new_value);
|
||||||
|
}
|
||||||
|
virtual void internal_set_value(float new_value) {
|
||||||
|
return set_value(new_value);
|
||||||
|
}
|
||||||
|
virtual void internal_set_value(int new_value) {
|
||||||
|
return set_value(new_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool clamp_value(float &value) {
|
||||||
|
if (has_min && (value < value_min)) {
|
||||||
|
value = value_min;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_max && (value > value_max)) {
|
||||||
|
value = value_max;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void change_string_value(const char *new_value, float old_value) {
|
||||||
|
if (value_string != nullptr) {
|
||||||
|
delete[] value_string;
|
||||||
|
value_string = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto new_len = strlen(new_value) + 1;
|
||||||
|
value_string = new char[new_len];
|
||||||
|
|
||||||
|
#if doghook_platform_windows()
|
||||||
|
strcpy_s(value_string, new_len, new_value);
|
||||||
|
#else
|
||||||
|
strncpy(value_string, new_value, new_len);
|
||||||
|
#endif
|
||||||
|
value_string_length = new_len;
|
||||||
|
|
||||||
|
if (change_callback != nullptr) change_callback(to_iconvar(), new_value, old_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// helper functions for converting from IConVar and to IConVar
|
||||||
|
static auto from_iconvar(IConVar *v) { return reinterpret_cast<ConCommandBase *>(reinterpret_cast<u8 *>(v) - 24); }
|
||||||
|
static auto to_iconvar(ConCommandBase *b) { return reinterpret_cast<IConVar *>(reinterpret_cast<u8 *>(b) + 24); }
|
||||||
|
auto to_iconvar() -> IConVar * { return ConCommandBase::to_iconvar(this); }
|
||||||
|
|
||||||
|
#define DEFINE_THUNK(type, name, real_name) \
|
||||||
|
static void __fastcall name(IConVar *ecx, void *edx, type v) { \
|
||||||
|
auto *real = ConCommandBase::from_iconvar(ecx); \
|
||||||
|
real->real_name(v); \
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFINE_THUNK(const char *, set_value_string_thunk, set_value);
|
||||||
|
DEFINE_THUNK(float, set_value_float_thunk, set_value);
|
||||||
|
DEFINE_THUNK(int, set_value_int_thunk, set_value);
|
||||||
|
#undef DEFINE_THUNK
|
||||||
|
|
||||||
|
// It doesnt look like IConVar::GetName and IConVar::IsFlagSet are called
|
||||||
|
static void __fastcall undefined_thunk(u8 *ecx, void *edx, int arg1) { assert(0); }
|
||||||
|
|
||||||
|
virtual void create_convar(char const *name, char const *default_value, u32 flags, char const *help_string,
|
||||||
|
bool has_min, float min, bool has_max, float max, ChangeCallbackFn change_callback) {
|
||||||
|
create_base(name, help_string, flags);
|
||||||
|
|
||||||
|
#if doghook_platform_windows()
|
||||||
|
{
|
||||||
|
// Set up the MI vtables properly
|
||||||
|
// IConvar.h
|
||||||
|
/*
|
||||||
|
virtual void SetValue( const char *pValue ) = 0;
|
||||||
|
virtual void SetValue( float flValue ) = 0;
|
||||||
|
virtual void SetValue( int nValue ) = 0;
|
||||||
|
virtual const char *GetName( void ) const = 0;
|
||||||
|
virtual bool IsFlagSet( int nFlag ) const = 0;
|
||||||
|
*/
|
||||||
|
|
||||||
|
// These all need to be properly thunked
|
||||||
|
static auto iconvar_vtable = []() {
|
||||||
|
auto ret = new void *[5];
|
||||||
|
ret[0] = &set_value_int_thunk;
|
||||||
|
ret[1] = &set_value_float_thunk;
|
||||||
|
ret[2] = &set_value_string_thunk;
|
||||||
|
|
||||||
|
ret[3] = &undefined_thunk;
|
||||||
|
ret[4] = &undefined_thunk;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}();
|
||||||
|
|
||||||
|
this->convar_vtable = iconvar_vtable;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
parent = this;
|
||||||
|
|
||||||
|
this->default_value = (default_value == nullptr) || (default_value[0] == '\0') ? "0.0" : default_value;
|
||||||
|
|
||||||
|
this->has_min = has_min;
|
||||||
|
this->value_min = min;
|
||||||
|
|
||||||
|
this->has_max = has_max;
|
||||||
|
this->value_max = max;
|
||||||
|
|
||||||
|
this->change_callback = change_callback;
|
||||||
|
|
||||||
|
set_value(this->default_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
ConCommandBase *next;
|
||||||
|
|
||||||
|
bool registered;
|
||||||
|
|
||||||
|
const char *name;
|
||||||
|
const char *help_string;
|
||||||
|
|
||||||
|
u32 flags;
|
||||||
|
|
||||||
|
static ConCommandBase *head;
|
||||||
|
|
||||||
|
// Convar is an mi class and therefore needs this
|
||||||
|
// TODO: is this windows only - what is the linux equivilent??
|
||||||
|
void *convar_vtable;
|
||||||
|
|
||||||
|
// Convar members
|
||||||
|
ConCommandBase *parent;
|
||||||
|
|
||||||
|
// Static data
|
||||||
|
const char *default_value;
|
||||||
|
|
||||||
|
// Value
|
||||||
|
// Dynamically allocated
|
||||||
|
char *value_string;
|
||||||
|
int value_string_length;
|
||||||
|
|
||||||
|
// Values
|
||||||
|
float value_float;
|
||||||
|
int value_int;
|
||||||
|
|
||||||
|
// Min/Max values
|
||||||
|
bool has_min;
|
||||||
|
float value_min;
|
||||||
|
bool has_max;
|
||||||
|
float value_max;
|
||||||
|
|
||||||
|
ChangeCallbackFn change_callback;
|
||||||
|
};
|
||||||
|
|
||||||
|
ConCommandBase *ConCommandBase::head;
|
||||||
|
} // namespace sdk
|
||||||
|
|
||||||
|
const ConvarBase *ConvarBase::head = nullptr;
|
||||||
|
|
||||||
|
void ConvarBase::tf_convar_changed(sdk::IConVar *iconvar, const char *old_string, float old_float) {
|
||||||
|
auto convar = sdk::ConCommandBase::from_iconvar(iconvar);
|
||||||
|
assert(convar);
|
||||||
|
|
||||||
|
for (auto c : ConvarBase::get_range()) {
|
||||||
|
if (c->tf_convar == convar) {
|
||||||
|
if (convar->registered == false) return;
|
||||||
|
if (c->init_complete == false) return;
|
||||||
|
|
||||||
|
auto modifiable = const_cast<ConvarBase *>(c);
|
||||||
|
auto was_clamped = modifiable->from_string(convar->value_string);
|
||||||
|
|
||||||
|
// Remove the callback when we call set_value to prevent recursion
|
||||||
|
// TODO: there is probably a better way to do this...
|
||||||
|
auto callback_backup = convar->change_callback;
|
||||||
|
convar->change_callback = nullptr;
|
||||||
|
if (was_clamped) convar->set_value(modifiable->to_string());
|
||||||
|
convar->change_callback = callback_backup;
|
||||||
|
|
||||||
|
Log::msg("Updated convar %s to '%s' (%s)", convar->get_name(), convar->value_string, was_clamped ? "clamped" : "not clamped");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ConvarBase::ConvarBase(const char *name, ConvarType type, const ConvarBase *parent) : parent(parent), t(type), next(head), init_complete(false) {
|
||||||
|
head = this;
|
||||||
|
|
||||||
|
#if doghook_platform_windows()
|
||||||
|
strcpy_s(internal_name, name);
|
||||||
|
#elif doghook_platform_linux()
|
||||||
|
strcpy(internal_name, name);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Create a tf convar based on this one
|
||||||
|
tf_convar = new sdk::ConCommandBase;
|
||||||
|
tf_convar->create_convar(name, "", 0, name, false, 0, false, 0, &ConvarBase::tf_convar_changed);
|
||||||
|
|
||||||
|
init_complete = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConvarBase::~ConvarBase() {
|
||||||
|
|
||||||
|
if (this == head) head = this->next;
|
||||||
|
|
||||||
|
for (auto c : ConvarBase::get_range()) {
|
||||||
|
auto modifiable = const_cast<ConvarBase *>(c);
|
||||||
|
if (c->next == this) modifiable->next = this->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConvarBase::init_all() {
|
||||||
|
assert(IFace<sdk::Cvar>());
|
||||||
|
|
||||||
|
// We have to do this goofy loop here as register_command() will
|
||||||
|
// change the `next` pointer to the next in its chain
|
||||||
|
// which will cause all kinds of problems for us
|
||||||
|
|
||||||
|
// TODO: this could also be fixed by using the ConvarBase linked list...
|
||||||
|
|
||||||
|
auto c = sdk::ConCommandBase::head;
|
||||||
|
while (c != nullptr) {
|
||||||
|
auto next = c->next;
|
||||||
|
|
||||||
|
IFace<sdk::Cvar>()->register_command(c);
|
||||||
|
|
||||||
|
c = next;
|
||||||
|
}
|
||||||
|
}
|
316
src/convar.hh
Normal file
316
src/convar.hh
Normal file
@ -0,0 +1,316 @@
|
|||||||
|
#include "platform.hh"
|
||||||
|
|
||||||
|
#include <cfloat>
|
||||||
|
#include <climits>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
// Using a tagged class system allows us to avoid the overhead
|
||||||
|
// of trying to dynamic_cast to each type and seeing if it works
|
||||||
|
// and then discarding that when we try the next
|
||||||
|
|
||||||
|
// Whilst it is more clumsy - it is faster.
|
||||||
|
|
||||||
|
// This will differ from the source version as we will split different
|
||||||
|
// Convar types into their own templated class so that you can static_cast
|
||||||
|
// to it once you know what you are dealing with.
|
||||||
|
|
||||||
|
namespace sdk {
|
||||||
|
class ConCommandBase;
|
||||||
|
class IConVar;
|
||||||
|
} // namespace sdk
|
||||||
|
|
||||||
|
enum class ConvarType {
|
||||||
|
Bool,
|
||||||
|
Int,
|
||||||
|
Float,
|
||||||
|
String,
|
||||||
|
Enum,
|
||||||
|
|
||||||
|
// Impossible...
|
||||||
|
None = -1,
|
||||||
|
};
|
||||||
|
|
||||||
|
class ConvarBase {
|
||||||
|
// Convar list
|
||||||
|
static const ConvarBase *head;
|
||||||
|
|
||||||
|
ConvarBase const *next;
|
||||||
|
ConvarBase const *parent;
|
||||||
|
|
||||||
|
char internal_name[128];
|
||||||
|
ConvarType t;
|
||||||
|
|
||||||
|
sdk::ConCommandBase *tf_convar;
|
||||||
|
|
||||||
|
bool init_complete;
|
||||||
|
|
||||||
|
static void tf_convar_changed(sdk::IConVar *convar, const char *old_string, float old_float);
|
||||||
|
|
||||||
|
public:
|
||||||
|
ConvarBase(const char *name, ConvarType type, const ConvarBase *parent);
|
||||||
|
virtual ~ConvarBase();
|
||||||
|
|
||||||
|
// virtual functions - so you dont have to check type, cast and then call
|
||||||
|
virtual bool from_string(const char *str) = 0;
|
||||||
|
virtual const char *to_string() const = 0;
|
||||||
|
|
||||||
|
auto name() const { return internal_name; }
|
||||||
|
auto type() const { return t; }
|
||||||
|
|
||||||
|
class Convar_Range {
|
||||||
|
public:
|
||||||
|
class Iterator {
|
||||||
|
const ConvarBase *current;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Iterator() : current(nullptr) {}
|
||||||
|
explicit Iterator(const ConvarBase *b) : current(b) {}
|
||||||
|
|
||||||
|
auto &operator++() {
|
||||||
|
current = current->next;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto operator*() const {
|
||||||
|
assert(current);
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto operator==(const Iterator &b) const { return current == b.current; }
|
||||||
|
auto operator!=(const Iterator &b) const { return !(*this == b); }
|
||||||
|
};
|
||||||
|
|
||||||
|
auto begin() const { return Iterator(head); }
|
||||||
|
|
||||||
|
auto end() const { return Iterator(nullptr); }
|
||||||
|
};
|
||||||
|
|
||||||
|
static auto get_range() { return Convar_Range(); }
|
||||||
|
|
||||||
|
static auto init_all() -> void;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class Convar;
|
||||||
|
|
||||||
|
template <>
|
||||||
|
class Convar<bool> : public ConvarBase {
|
||||||
|
bool value;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Convar(const char *name, const ConvarBase *parent) : ConvarBase(name, ConvarType::Bool, parent), value(false) {}
|
||||||
|
|
||||||
|
Convar(const char *name, bool value, const ConvarBase *parent) : Convar(name, parent) {
|
||||||
|
this->value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool from_string(const char *str) override final {
|
||||||
|
assert(str);
|
||||||
|
|
||||||
|
if (_stricmp(str, "false") == 0) {
|
||||||
|
value = false;
|
||||||
|
return false;
|
||||||
|
} else if (_stricmp(str, "true") == 0) {
|
||||||
|
value = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
value = atoi(str);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *to_string() const override final {
|
||||||
|
return value ? "true" : "false";
|
||||||
|
}
|
||||||
|
|
||||||
|
operator bool() const { return value; }
|
||||||
|
|
||||||
|
auto operator=(bool v) {
|
||||||
|
value = v;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
class Convar<int> : public ConvarBase {
|
||||||
|
|
||||||
|
int value;
|
||||||
|
|
||||||
|
// min + max values (INT_MAX == no value)
|
||||||
|
int min_value;
|
||||||
|
int max_value;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static constexpr int no_value = INT_MAX;
|
||||||
|
|
||||||
|
Convar(const char *name, const ConvarBase *parent) : ConvarBase(name, ConvarType::Int, parent),
|
||||||
|
value(0), min_value(no_value), max_value(no_value) {}
|
||||||
|
|
||||||
|
// use no_value if you want their to be no min/max
|
||||||
|
Convar(const char *name, int value, int min_value, int max_value, const ConvarBase *parent) : Convar(name, parent) {
|
||||||
|
this->value = value;
|
||||||
|
this->min_value = min_value;
|
||||||
|
this->max_value = max_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool from_string(const char *str) override {
|
||||||
|
assert(str);
|
||||||
|
|
||||||
|
auto new_value = atoi(str);
|
||||||
|
|
||||||
|
if (min_value != no_value) {
|
||||||
|
if (new_value < min_value) {
|
||||||
|
value = min_value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (max_value != no_value) {
|
||||||
|
if (new_value > max_value) {
|
||||||
|
value = max_value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
value = new_value;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *to_string() const override {
|
||||||
|
static u32 cur_index = 0;
|
||||||
|
static char temp[20][8];
|
||||||
|
|
||||||
|
cur_index += 1;
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
memset(temp[cur_index], 0, sizeof(temp));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if doghook_platform_windows()
|
||||||
|
_itoa_s(value, temp[cur_index], 10);
|
||||||
|
#else
|
||||||
|
sprintf(temp[cur_index], "%d", value);
|
||||||
|
#endif
|
||||||
|
return temp[cur_index];
|
||||||
|
}
|
||||||
|
|
||||||
|
operator int() const { return value; }
|
||||||
|
|
||||||
|
auto operator=(int v) {
|
||||||
|
value = v;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
class Convar<float> : public ConvarBase {
|
||||||
|
|
||||||
|
float value;
|
||||||
|
|
||||||
|
// min + max values (INT_MAX == no value)
|
||||||
|
float min_value;
|
||||||
|
float max_value;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static constexpr float no_value = FLT_MAX;
|
||||||
|
|
||||||
|
Convar(const char *name, const ConvarBase *parent) : ConvarBase(name, ConvarType::Float, parent),
|
||||||
|
value(0), min_value(no_value), max_value(no_value) {}
|
||||||
|
|
||||||
|
// use no_value if you want their to be no min/max
|
||||||
|
Convar(const char *name, float value, float min_value, float max_value, const ConvarBase *parent) : Convar(name, parent) {
|
||||||
|
this->value = value;
|
||||||
|
this->min_value = min_value;
|
||||||
|
this->max_value = max_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool from_string(const char *str) override {
|
||||||
|
assert(str);
|
||||||
|
|
||||||
|
auto new_value = static_cast<float>(atof(str));
|
||||||
|
|
||||||
|
if (min_value != no_value) {
|
||||||
|
if (value < min_value) {
|
||||||
|
value = min_value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (max_value != no_value) {
|
||||||
|
if (value > max_value) {
|
||||||
|
value = min_value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
value = new_value;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *to_string() const override {
|
||||||
|
static u32 cur_index = 0;
|
||||||
|
static char temp[20][8];
|
||||||
|
|
||||||
|
cur_index += 1;
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
memset(temp[cur_index], 0, sizeof(temp));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// TODO: this is clumsy
|
||||||
|
return std::to_string(value).c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
operator float() const { return value; }
|
||||||
|
|
||||||
|
auto operator=(float v) {
|
||||||
|
value = v;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
class Convar<char *> : public ConvarBase {
|
||||||
|
char *value;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Convar(const char *name, const ConvarBase *parent) : ConvarBase(name, ConvarType::String, parent),
|
||||||
|
value(nullptr) {}
|
||||||
|
|
||||||
|
Convar(const char *name, const char *value, const ConvarBase *parent) : Convar(name, parent) {
|
||||||
|
auto size = strlen(value) + 1;
|
||||||
|
this->value = new char[size];
|
||||||
|
#if doghook_platform_windows()
|
||||||
|
strcpy_s(this->value, size, value);
|
||||||
|
#elif doghook_platform_linux()
|
||||||
|
strncpy(this->value, value, size);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
~Convar() {
|
||||||
|
if (value != nullptr) {
|
||||||
|
delete[] value;
|
||||||
|
value = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool from_string(const char *str) override {
|
||||||
|
if (value != nullptr) delete[] value;
|
||||||
|
|
||||||
|
auto size = strlen(str) + 1;
|
||||||
|
value = new char[size];
|
||||||
|
|
||||||
|
#if doghook_platform_windows()
|
||||||
|
strcpy_s(this->value, size, str);
|
||||||
|
#elif doghook_platform_linux()
|
||||||
|
strncpy(this->value, str, size);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *to_string() const override {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
};
|
125
src/doghook.cc
Normal file
125
src/doghook.cc
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
#include "stdafx.hh"
|
||||||
|
|
||||||
|
#include "gamesystem.hh"
|
||||||
|
#include "log.hh"
|
||||||
|
|
||||||
|
#include "class_id.hh"
|
||||||
|
#include "convar.hh"
|
||||||
|
#include "hooks.hh"
|
||||||
|
#include "interface.hh"
|
||||||
|
#include "netvar.hh"
|
||||||
|
#include "player.hh"
|
||||||
|
#include "sdk.hh"
|
||||||
|
#include "vfunc.hh"
|
||||||
|
|
||||||
|
// Singleton for doing init / deinit of doghook
|
||||||
|
// and dealing with hooks from gamesystem
|
||||||
|
|
||||||
|
class Doghook : public GameSystem {
|
||||||
|
bool inited = false;
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool init() override {
|
||||||
|
// Guard against having init() called by the game and our constructor
|
||||||
|
static bool init_happened = false;
|
||||||
|
if (init_happened) return true;
|
||||||
|
|
||||||
|
Log::msg("init()");
|
||||||
|
init_happened = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void post_init() override {
|
||||||
|
Log::msg("post_init()");
|
||||||
|
|
||||||
|
// Get interfaces here before init_all has a chance to do anything
|
||||||
|
|
||||||
|
IFace<sdk::Client>().set_from_interface("client", "VClient");
|
||||||
|
IFace<sdk::Engine>().set_from_interface("engine", "VEngineClient");
|
||||||
|
IFace<sdk::EntList>().set_from_interface("client", "VClientEntityList");
|
||||||
|
IFace<sdk::Input>().set_from_pointer(**reinterpret_cast<sdk::Input ***>(
|
||||||
|
vfunc::get_func<u8 *>(IFace<sdk::Client>().get(), 15, 0) + 0x2));
|
||||||
|
IFace<sdk::Cvar>().set_from_interface("vstdlib", "VEngineCvar");
|
||||||
|
|
||||||
|
// TODO linux signatures
|
||||||
|
IFace<sdk::ClientMode>().set_from_pointer(
|
||||||
|
*signature::find_pattern<sdk::ClientMode **>(
|
||||||
|
"client", "B9 ? ? ? ? A3 ? ? ? ? E8 ? ? ? ? 68 ? ? ? ? E8 ? ? ? ? 83 C4 04 C7 05", 1));
|
||||||
|
IFace<sdk::ModelInfo>().set_from_interface("engine", "VModelInfoClient");
|
||||||
|
IFace<sdk::Trace>().set_from_interface("engine", "EngineTraceClient");
|
||||||
|
IFace<sdk::DebugOverlay>().set_from_interface("engine", "VDebugOverlay");
|
||||||
|
IFace<sdk::PlayerInfoManager>().set_from_interface("server", "PlayerInfoManager");
|
||||||
|
|
||||||
|
IFace<sdk::Globals>().set_from_pointer(IFace<sdk::PlayerInfoManager>()->globals());
|
||||||
|
|
||||||
|
auto globals_server_address = (u32)IFace<sdk::Globals>().get();
|
||||||
|
|
||||||
|
// TODO: this globals_real_address is windows only!
|
||||||
|
if constexpr (doghook_platform::windows()) {
|
||||||
|
auto globals_real_address = (u32)*signature::find_pattern<sdk::Globals **>("engine", "A1 ? ? ? ? 8B 11 68", 8);
|
||||||
|
IFace<sdk::Globals>().set_from_pointer((sdk::Globals *)globals_real_address);
|
||||||
|
}
|
||||||
|
|
||||||
|
IFace<sdk::GameMovement>().set_from_interface("client", "GameMovement");
|
||||||
|
IFace<sdk::Prediction>().set_from_interface("client", "VClientPrediction");
|
||||||
|
IFace<sdk::MoveHelper>().set_from_pointer(
|
||||||
|
*signature::find_pattern<sdk::MoveHelper **>(
|
||||||
|
"client", "8B 0D ? ? ? ? 8B 01 FF 50 28 56", 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
void process_attach() {
|
||||||
|
Log::msg("process_attach()");
|
||||||
|
|
||||||
|
// make sure that the netvars are initialised
|
||||||
|
// becuase their dynamic initialiser could be after the
|
||||||
|
// gamesystems one
|
||||||
|
sdk::Netvar::init_all();
|
||||||
|
|
||||||
|
// register all convars now that we have the interfaces we need
|
||||||
|
ConvarBase::init_all();
|
||||||
|
|
||||||
|
// at this point we are now inited and ready to go!
|
||||||
|
inited = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void shutdown() override {}
|
||||||
|
|
||||||
|
void level_init_pre_entity() override { Log::msg("init_pre_entity()"); }
|
||||||
|
void level_init_post_entity() override {
|
||||||
|
Log::msg("level_init_post_entity");
|
||||||
|
|
||||||
|
// Make sure that all our class_ids are correct
|
||||||
|
// This will only do anything on debug builds and not on release builds.
|
||||||
|
|
||||||
|
// This needs to be done here becuase classids arent initialised before we are in game
|
||||||
|
sdk::class_id::internal_checker::ClassIDChecker::check_all_correct();
|
||||||
|
}
|
||||||
|
auto level_shutdown_pre_clear_steam_api_context() -> void override { Log::msg("level_shutdown_pre_clear_steam_api_context"); }
|
||||||
|
auto level_shutdown_pre_entity() -> void override {
|
||||||
|
Log::msg("level_shutdown_pre_entity");
|
||||||
|
}
|
||||||
|
auto level_shutdown_post_entity() -> void override { Log::msg("level_shutdown_post_entity"); }
|
||||||
|
|
||||||
|
// update is called from CHLClient_HudUpdate
|
||||||
|
// in theory we should be able to render here
|
||||||
|
// and be perfectly ok
|
||||||
|
// HOWEVER: it might be better to do this at frame_end()
|
||||||
|
void update([[maybe_unused]] float frametime) override {
|
||||||
|
if (inited != true || IFace<sdk::Engine>()->in_game() != true) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Doghook() {
|
||||||
|
GameSystem::add_all();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Doghook doghook;
|
||||||
|
|
||||||
|
#if doghook_platform_windows()
|
||||||
|
auto __stdcall doghook_process_attach([[maybe_unused]] void *hmodule) -> u32 {
|
||||||
|
// TODO: pass module over to the gamesystem
|
||||||
|
doghook.process_attach();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
48
src/gamesystem.cc
Normal file
48
src/gamesystem.cc
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
#include "stdafx.hh"
|
||||||
|
|
||||||
|
#include "gamesystem.hh"
|
||||||
|
#include "signature.hh"
|
||||||
|
|
||||||
|
static GameSystem *head = nullptr;
|
||||||
|
GameSystem::GameSystem() {
|
||||||
|
next = head;
|
||||||
|
head = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
GameSystem::~GameSystem() {
|
||||||
|
// TODO: remove the gamesystem!
|
||||||
|
}
|
||||||
|
|
||||||
|
auto GameSystem::add_all() -> void {
|
||||||
|
using AddFn = void (*)(IGameSystem *);
|
||||||
|
AddFn add_fn;
|
||||||
|
|
||||||
|
if constexpr (doghook_platform::windows()) {
|
||||||
|
add_fn = reinterpret_cast<AddFn>(
|
||||||
|
signature::resolve_callgate(
|
||||||
|
signature::find_pattern("client", "E8 ? ? ? ? 83 C4 04 8B 76 04 85 F6 75 D0")));
|
||||||
|
} else if constexpr (doghook_platform::linux()) {
|
||||||
|
add_fn = reinterpret_cast<AddFn>(
|
||||||
|
signature::resolve_callgate(
|
||||||
|
signature::find_pattern("client", "E8 ? ? ? ? 8B 5B 04 85 DB 75 C1")));
|
||||||
|
} else if constexpr (doghook_platform::osx()) {
|
||||||
|
add_fn = reinterpret_cast<AddFn>(
|
||||||
|
signature::resolve_callgate(
|
||||||
|
signature::find_pattern("client", "E8 ? ? ? ? 8B 7F 04 85 FF 75 A1")));
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(add_fn);
|
||||||
|
|
||||||
|
for (auto system = head; system != nullptr; system = system->next) {
|
||||||
|
// Call original Add() function
|
||||||
|
((void (*)(IGameSystem *))add_fn)(system);
|
||||||
|
system->init();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto system = head; system != nullptr; system = system->next) {
|
||||||
|
system->post_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset the list
|
||||||
|
head = nullptr;
|
||||||
|
}
|
133
src/gamesystem.hh
Normal file
133
src/gamesystem.hh
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// These classes must remain by these names in order to fufill some rtti hacks when adding gamesystems
|
||||||
|
// DO NOT CHANGE THEM
|
||||||
|
|
||||||
|
class IGameSystem {
|
||||||
|
public:
|
||||||
|
// GameSystems are expected to implement these methods.
|
||||||
|
virtual char const *name() = 0;
|
||||||
|
|
||||||
|
// Init, shutdown
|
||||||
|
// return true on success. false to abort DLL init!
|
||||||
|
virtual bool init() = 0;
|
||||||
|
virtual void post_init() = 0;
|
||||||
|
virtual void shutdown() = 0;
|
||||||
|
|
||||||
|
// Level init, shutdown
|
||||||
|
virtual void level_init_pre_entity() = 0;
|
||||||
|
// entities are created / spawned / precached here
|
||||||
|
virtual void level_init_post_entity() = 0;
|
||||||
|
|
||||||
|
virtual void level_shutdown_pre_clear_steam_api_context() = 0;
|
||||||
|
virtual void level_shutdown_pre_entity() = 0;
|
||||||
|
// Entities are deleted / released here...
|
||||||
|
virtual void level_shutdown_post_entity() = 0;
|
||||||
|
// end of level shutdown
|
||||||
|
|
||||||
|
// Called during game save
|
||||||
|
virtual void on_save() = 0;
|
||||||
|
|
||||||
|
// Called during game restore, after the local player has connected and entities have been fully restored
|
||||||
|
virtual void on_restore() = 0;
|
||||||
|
|
||||||
|
// Called every frame. It's safe to remove an igamesystem from within this callback.
|
||||||
|
virtual void safe_remove_if_desired() = 0;
|
||||||
|
|
||||||
|
virtual bool is_per_frame() = 0;
|
||||||
|
|
||||||
|
// destructor, cleans up automagically....
|
||||||
|
virtual ~IGameSystem(){};
|
||||||
|
|
||||||
|
// Client systems can use this to get at the map name
|
||||||
|
static const char *map_name();
|
||||||
|
|
||||||
|
// These methods are used to add and remove server systems from the
|
||||||
|
// main server loop. The systems are invoked in the order in which
|
||||||
|
// they are added.
|
||||||
|
private:
|
||||||
|
static void add(IGameSystem *pSys);
|
||||||
|
static void remove(IGameSystem *pSys);
|
||||||
|
static void remove_all();
|
||||||
|
|
||||||
|
// These methods are used to initialize, shutdown, etc all systems
|
||||||
|
static bool init_all_systems();
|
||||||
|
static void post_init_all_systems();
|
||||||
|
static void shutdown_all_systems();
|
||||||
|
static void level_init_pre_entity_all_systems(char const *pMapName);
|
||||||
|
static void level_init_post_all_systems();
|
||||||
|
static void level_shutdown_pre_clear_steam_api_context_all_systems(); // Called prior to steamgameserverapicontext->Clear()
|
||||||
|
static void level_shutdown_pre_entity_all_systems();
|
||||||
|
static void level_shutdown_post_entity_all_systems();
|
||||||
|
|
||||||
|
static void on_save_all_systems();
|
||||||
|
static void on_restore_all_systems();
|
||||||
|
|
||||||
|
static void safe_remove_if_desired_all_systems();
|
||||||
|
|
||||||
|
static void pre_render_all_systems();
|
||||||
|
static void update_all_systems(float frametime);
|
||||||
|
static void post_render_all_systems();
|
||||||
|
};
|
||||||
|
|
||||||
|
class IGameSystemPerFrame : public IGameSystem {
|
||||||
|
public:
|
||||||
|
// destructor, cleans up automagically....
|
||||||
|
virtual ~IGameSystemPerFrame(){};
|
||||||
|
|
||||||
|
// Called before rendering
|
||||||
|
virtual void pre_render() = 0;
|
||||||
|
|
||||||
|
// Gets called each frame
|
||||||
|
virtual void update(float frametime) = 0;
|
||||||
|
|
||||||
|
// Called after rendering
|
||||||
|
virtual void post_render() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Quick and dirty server system for users who don't care about precise ordering
|
||||||
|
// and usually only want to implement a few of the callbacks
|
||||||
|
class CBaseGameSystemPerFrame : public IGameSystemPerFrame {
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual const char *name() override { return "unnamed"; }
|
||||||
|
|
||||||
|
// Init, shutdown
|
||||||
|
// return true on success. false to abort DLL init!
|
||||||
|
virtual bool init() override { return true; }
|
||||||
|
virtual void post_init() override {}
|
||||||
|
virtual void shutdown() override {}
|
||||||
|
|
||||||
|
// Level init, shutdown
|
||||||
|
virtual void level_init_pre_entity() override {}
|
||||||
|
virtual void level_init_post_entity() override {}
|
||||||
|
virtual void level_shutdown_pre_clear_steam_api_context() override {}
|
||||||
|
virtual void level_shutdown_pre_entity() override {}
|
||||||
|
virtual void level_shutdown_post_entity() override {}
|
||||||
|
|
||||||
|
virtual void on_save() override {}
|
||||||
|
virtual void on_restore() override {}
|
||||||
|
virtual void safe_remove_if_desired() override {}
|
||||||
|
|
||||||
|
virtual bool is_per_frame() override { return true; }
|
||||||
|
|
||||||
|
// Called before rendering
|
||||||
|
virtual void pre_render() override {}
|
||||||
|
|
||||||
|
// Gets called each frame
|
||||||
|
virtual void update(float) override {}
|
||||||
|
|
||||||
|
// Called after rendering
|
||||||
|
virtual void post_render() override {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class GameSystem : public CBaseGameSystemPerFrame {
|
||||||
|
GameSystem *next;
|
||||||
|
|
||||||
|
public:
|
||||||
|
GameSystem();
|
||||||
|
virtual ~GameSystem();
|
||||||
|
|
||||||
|
// add all functions that are in the linked list
|
||||||
|
static void add_all();
|
||||||
|
};
|
21
src/log.cc
Normal file
21
src/log.cc
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#include "stdafx.hh"
|
||||||
|
|
||||||
|
#include "log.hh"
|
||||||
|
#include "signature.hh"
|
||||||
|
|
||||||
|
auto Log::msg(const char *format, ...) -> void {
|
||||||
|
|
||||||
|
char buffer[1024];
|
||||||
|
va_list vlist;
|
||||||
|
va_start(vlist, format);
|
||||||
|
vsnprintf(buffer, 1024, format, vlist);
|
||||||
|
va_end(vlist);
|
||||||
|
|
||||||
|
using MessageFn = void (*)(const char *format, ...);
|
||||||
|
|
||||||
|
static auto msg_fn = (MessageFn)signature::resolve_import(signature::resolve_library("tier0"), "Msg");
|
||||||
|
|
||||||
|
msg_fn("[Woof] %s\n", buffer);
|
||||||
|
|
||||||
|
printf("[Woof] %s\n", buffer);
|
||||||
|
}
|
5
src/log.hh
Normal file
5
src/log.hh
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Log {
|
||||||
|
void msg(const char *format, ...);
|
||||||
|
}
|
@ -4,8 +4,6 @@
|
|||||||
|
|
||||||
extern u32 __stdcall doghook_process_attach(void *a);
|
extern u32 __stdcall doghook_process_attach(void *a);
|
||||||
|
|
||||||
u32 __stdcall doghook_process_attach(void *a) { return 0; }
|
|
||||||
|
|
||||||
__declspec(dllexport) BOOL APIENTRY DllMain(HMODULE hModule,
|
__declspec(dllexport) BOOL APIENTRY DllMain(HMODULE hModule,
|
||||||
DWORD reason,
|
DWORD reason,
|
||||||
LPVOID lpReserved) {
|
LPVOID lpReserved) {
|
||||||
@ -18,6 +16,8 @@ __declspec(dllexport) BOOL APIENTRY DllMain(HMODULE hModule,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
// TODO: send process attach over to gamesystem
|
||||||
|
|
||||||
void __attribute__((constructor)) startup() {}
|
void __attribute__((constructor)) startup() {}
|
||||||
|
|
||||||
void __attribute__((destructor)) shutdown() {}
|
void __attribute__((destructor)) shutdown() {}
|
||||||
|
@ -63,4 +63,8 @@ inline constexpr bool gcc() { return doghook_platform_gcc(); }
|
|||||||
#define __fastcall
|
#define __fastcall
|
||||||
#define __stdcall
|
#define __stdcall
|
||||||
#define __cdecl
|
#define __cdecl
|
||||||
|
|
||||||
|
// TODO: make a function wrapper please
|
||||||
|
#define _stricmp strcasecmp
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -239,6 +239,7 @@ public:
|
|||||||
|
|
||||||
auto get_verified_user_cmd(u32 sequence_number) -> class VerifiedCmd * {
|
auto get_verified_user_cmd(u32 sequence_number) -> class VerifiedCmd * {
|
||||||
// 03 B7 ? ? ? ? 8D 04 88
|
// 03 B7 ? ? ? ? 8D 04 88
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user