Merge pull request #199 from XLabsProject/release/v1.0.4

Release v1.0.4
This commit is contained in:
Maurice Heumann 2021-05-20 13:31:52 +02:00 committed by GitHub
commit 9a6089ec56
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 780 additions and 27 deletions

3
.gitmodules vendored
View File

@ -41,3 +41,6 @@
[submodule "deps/lua"]
path = deps/lua
url = https://github.com/lua/lua.git
[submodule "deps/stb"]
path = deps/stb
url = https://github.com/nothings/stb.git

View File

@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [v1.0.4] - 2021-05-20
### Added
- Add game matrix access [#161](https://github.com/XLabsProject/s1x-client/issues/161)
- Add custom camo support [#185](https://github.com/XLabsProject/s1x-client/issues/185)
### Fixed
- Fatal error with test build [#193](https://github.com/XLabsProject/s1x-client/issues/193)
### Pull Requests
- Fix service not available disconnect [#172](https://github.com/XLabsProject/s1x-client/pull/172) ([@Joelrau](https://github.com/Joelrau))
- Script struct support #132 [#177](https://github.com/XLabsProject/s1x-client/pull/177) ([@fedddddd](https://github.com/fedddddd))
- Use sizeof operator more often [#181](https://github.com/XLabsProject/s1x-client/pull/181) ([@diamante0018](https://github.com/diamante0018))
- Script function support [#183](https://github.com/XLabsProject/s1x-client/pull/183) ([@fedddddd](https://github.com/fedddddd))
## [v1.0.3] - 2021-05-04
### Added
@ -126,7 +144,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Discord RPC - party size + party size max [#59](https://github.com/XLabsProject/s1x-client/pull/59) ([@mjkzy](https://github.com/mjkzy))
- discord presence - host name address [#64](https://github.com/XLabsProject/s1x-client/pull/64) ([@mjkzy](https://github.com/mjkzy))
[Unreleased]: https://github.com/XLabsProject/s1x-client/compare/v1.0.3...HEAD
[Unreleased]: https://github.com/XLabsProject/s1x-client/compare/v1.0.4...HEAD
[v1.0.4]: https://github.com/XLabsProject/s1x-client/compare/v1.0.3...v1.0.4
[v1.0.3]: https://github.com/XLabsProject/s1x-client/compare/v1.0.2...v1.0.3

2
deps/GSL vendored

@ -1 +1 @@
Subproject commit ef0ffefe525a6219ff245d19a832ce06f3fd3504
Subproject commit c1cbb41b428f15e53454682a45f03ea31f1da0a7

2
deps/asmjit vendored

@ -1 +1 @@
Subproject commit 0dd16b0a98ae1da48563c9cc62f757a9e6bbe9b6
Subproject commit 8ee4c76ee3bf4c33478347caefc5d4f7f0e97992

19
deps/premake/stb.lua vendored Normal file
View File

@ -0,0 +1,19 @@
stb = {
source = path.join(dependencies.basePath, "stb"),
}
function stb.import()
stb.includes()
end
function stb.includes()
includedirs {
stb.source
}
end
function stb.project()
end
table.insert(dependencies, stb)

2
deps/rapidjson vendored

@ -1 +1 @@
Subproject commit e0f68a435610e70ab5af44fc6a90523d69b210b3
Subproject commit 17aa824c928ea111e9b12a61e06d98335ce98f15

2
deps/sol2 vendored

@ -1 +1 @@
Subproject commit f56b3c698c2116f41aad3bf731ba8400e68c498b
Subproject commit 430b55a49609daacea6fd3ee2c9d137db4db9c83

1
deps/stb vendored Submodule

@ -0,0 +1 @@
Subproject commit c9064e317699d2e495f36ba4f9ac037e88ee371a

View File

@ -430,6 +430,7 @@ namespace demonware
utils::hook::inject(0x140038A07, "http://prod.umbrella.demonware.net/v1.0/");
utils::hook::set<uint8_t>(0x140437CC0, 0xC3); // SV_SendMatchData
utils::hook::set<uint8_t>(0x140560D70, 0xC3); // Live_CheckForFullDisconnect
}
void pre_destroy() override

View File

@ -63,7 +63,7 @@ namespace game_console
void clear()
{
strncpy_s(con.buffer, "", 256);
strncpy_s(con.buffer, "", sizeof(con.buffer));
con.cursor = 0;
fixed_input = "";
@ -550,7 +550,7 @@ namespace game_console
if (history_index != -1)
{
strncpy_s(con.buffer, history.at(history_index).c_str(), 0x100);
strncpy_s(con.buffer, history.at(history_index).c_str(), sizeof(con.buffer));
con.cursor = static_cast<int>(strlen(con.buffer));
}
}
@ -565,7 +565,7 @@ namespace game_console
if (history_index != -1)
{
strncpy_s(con.buffer, history.at(history_index).c_str(), 0x100);
strncpy_s(con.buffer, history.at(history_index).c_str(), sizeof(con.buffer));
con.cursor = static_cast<int>(strlen(con.buffer));
}
}
@ -720,7 +720,7 @@ namespace game_console
con.output_visible = false;
con.display_line_offset = 0;
con.line_count = 0;
strncpy_s(con.buffer, "", 256);
strncpy_s(con.buffer, "", sizeof(con.buffer));
con.globals.x = 0.0f;
con.globals.y = 0.0f;

View File

@ -0,0 +1,135 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "images.hpp"
#include "console.hpp"
#include "scheduler.hpp"
#include <utils/hook.hpp>
#include <utils/string.hpp>
#include <utils/image.hpp>
#include <utils/io.hpp>
#include <utils/concurrency.hpp>
namespace images
{
namespace
{
utils::hook::detour load_texture_hook;
utils::hook::detour setup_texture_hook;
utils::concurrency::container<std::unordered_map<std::string, std::string>> overriden_textures;
static_assert(sizeof(game::GfxImage) == 104);
static_assert(offsetof(game::GfxImage, name) == (sizeof(game::GfxImage) - sizeof(void*)));
static_assert(offsetof(game::GfxImage, pixelData) == 56);
static_assert(offsetof(game::GfxImage, width) == 44);
static_assert(offsetof(game::GfxImage, height) == 46);
static_assert(offsetof(game::GfxImage, depth) == 48);
static_assert(offsetof(game::GfxImage, numElements) == 50);
std::optional<std::string> load_image(game::GfxImage* image)
{
std::string data{};
overriden_textures.access([&](const std::unordered_map<std::string, std::string>& textures)
{
if (const auto i = textures.find(image->name); i != textures.end())
{
data = i->second;
}
});
if (data.empty() && !utils::io::read_file(utils::string::va("s1x/images/%s.png", image->name), &data))
{
return {};
}
return {std::move(data)};
}
std::optional<utils::image> load_raw_image_from_file(game::GfxImage* image)
{
const auto image_file = load_image(image);
if (!image_file)
{
return {};
}
return utils::image(*image_file);
}
bool load_custom_texture(game::GfxImage* image)
{
auto raw_image = load_raw_image_from_file(image);
if (!raw_image)
{
return false;
}
image->imageFormat = 0x1000003;
image->resourceSize = -1;
D3D11_SUBRESOURCE_DATA data{};
data.SysMemPitch = raw_image->get_width() * 4;
data.SysMemSlicePitch = data.SysMemPitch * raw_image->get_height();
data.pSysMem = raw_image->get_buffer();
game::Image_Setup(image, raw_image->get_width(), raw_image->get_height(), image->depth, image->numElements, image->imageFormat,
DXGI_FORMAT_R8G8B8A8_UNORM, image->name, &data);
return true;
}
void load_texture_stub(game::GfxImageLoadDef** load_def, game::GfxImage* image)
{
#if defined(DEV_BUILD) && defined(DEBUG)
printf("Loading: %s\n", image->name);
#endif
try
{
if (load_custom_texture(image))
{
return;
}
}
catch(std::exception& e)
{
console::error("Failed to load image %s: %s\n", image->name, e.what());
}
load_texture_hook.invoke(load_def, image);
}
int setup_texture_stub(game::GfxImage* image, void* a2, void* a3)
{
if(image->resourceSize == -1)
{
return 0;
}
return setup_texture_hook.invoke<bool>(image, a2, a3);
}
}
void override_texture(std::string name, std::string data)
{
overriden_textures.access([&](std::unordered_map<std::string, std::string>& textures)
{
textures[std::move(name)] = std::move(data);
});
}
class component final : public component_interface
{
public:
void post_unpack() override
{
if (game::environment::is_dedi()) return;
setup_texture_hook.create(SELECT_VALUE(0x14002A560, 0x140054370), setup_texture_stub);
load_texture_hook.create(SELECT_VALUE(0x140484970, 0x1405A21F0), load_texture_stub);
}
};
}
REGISTER_COMPONENT(images::component)

View File

@ -0,0 +1,6 @@
#pragma once
namespace images
{
void override_texture(std::string name, std::string data);
}

View File

@ -52,7 +52,7 @@ namespace logger
va_end(ap);
console::error(buffer);
console::error("Error: %s\n", buffer);
}
com_error_hook.invoke<void>(error, "%s", buffer);
@ -159,13 +159,18 @@ namespace logger
public:
void post_unpack() override
{
if (!game::environment::is_mp()) return;
if (game::environment::is_mp())
{
nullsub_56();
sub_1400E7420();
}
nullsub_56();
sub_1400E7420();
if (!game::environment::is_sp())
{
utils::hook::call(0x1404D8543, print_com_error);
}
utils::hook::call(0x1404D8543, print_com_error);
com_error_hook.create(0x1403CE480, com_error_stub);
com_error_hook.create(game::Com_Error, com_error_stub);
}
};
}

View File

@ -1,6 +1,7 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "motd.hpp"
#include "images.hpp"
#include <utils/hook.hpp>
#include <utils/http.hpp>
@ -35,6 +36,14 @@ namespace motd
void post_load() override
{
motd_future = utils::http::get_data_async("https://xlabs.dev/s1/motd.txt");
std::thread([]()
{
auto data = utils::http::get_data("https://xlabs.dev/s1/motd.png");
if(data)
{
images::override_texture("iotd_image", data.value());
}
}).detach();
}
};
}

View File

@ -153,7 +153,7 @@ namespace party
if (game::mp::g_entities[i].client)
{
char client_name[16] = {0};
strncpy_s(client_name, game::mp::g_entities[i].client->name, 16);
strncpy_s(client_name, game::mp::g_entities[i].client->name, sizeof(client_name));
game::I_CleanStr(client_name);
if (client_name == name)

View File

@ -77,7 +77,7 @@ namespace rcon
const auto client = &game::mp::svs_clients[i];
char clean_name[32] = { 0 };
strncpy_s(clean_name, client->name, 32);
strncpy_s(clean_name, client->name, sizeof(clean_name));
game::I_CleanStr(clean_name);
if (client->header.state >= 1)

View File

@ -14,6 +14,29 @@ namespace security
reinterpret_cast<void(*)(int, int, int)>(0x140536A60)(localclient, index1, index2);
}
}
void remap_cached_entities(game::mp::cachedSnapshot_t& snapshot)
{
static bool printed = false;
if(snapshot.num_clients > 1200 && !printed)
{
printed = true;
printf("Too many entities (%d)... remapping!\n", snapshot.num_clients);
}
snapshot.num_clients = std::min(snapshot.num_clients, 1200);
}
void remap_cached_entities_stub(utils::hook::assembler& a)
{
a.pushad64();
a.mov(rcx, rbx);
a.call_aligned(remap_cached_entities);
a.popad64();
a.jmp(0x14044DE51);
}
}
class component final : public component_interface
@ -25,6 +48,9 @@ namespace security
// Patch vulnerability in PlayerCards_SetCachedPlayerData
utils::hook::call(0x1401BB909, set_cached_playerdata_stub);
// Patch entity overflow
utils::hook::jump(0x14044DE3A, assemble(remap_cached_entities_stub), true);
}
};
}

View File

@ -18,7 +18,7 @@ namespace videos
if (vid != video_replaces.end())
{
char path[256];
game::Sys_BuildAbsPath(path, 256, game::SF_VIDEO, vid->second.data(), ".bik");
game::Sys_BuildAbsPath(path, sizeof(path), game::SF_VIDEO, vid->second.data(), ".bik");
if (game::Sys_FileExists(path))
{

View File

@ -111,6 +111,25 @@ namespace scripting
return call_function(name, arguments);
}
script_value exec_ent_thread(const entity& entity, const char* pos, const std::vector<script_value>& arguments)
{
const auto id = entity.get_entity_id();
stack_isolation _;
for (auto i = arguments.rbegin(); i != arguments.rend(); ++i)
{
scripting::push_value(*i);
}
game::AddRefToObject(id);
const auto local_id = game::AllocThread(id);
const auto result = game::VM_Execute(local_id, pos, (unsigned int)arguments.size());
game::RemoveRefToObject(result);
return get_return_value();
}
static std::unordered_map<unsigned int, std::unordered_map<std::string, script_value>> custom_fields;
script_value get_custom_field(const entity& entity, const std::string& field)

View File

@ -21,6 +21,8 @@ namespace scripting
return call<script_value>(name, arguments).as<T>();
}
script_value exec_ent_thread(const entity& entity, const char* pos, const std::vector<script_value>& arguments);
void clear_entity_fields(const entity& entity);
void clear_custom_fields();

View File

@ -1490,4 +1490,33 @@ namespace scripting
{"sub_140320b40", 34155},
{"sub_140333710", 34156},
};
std::unordered_map<std::string, unsigned> token_map =
{
{"CodeCallback_BulletHitEntity", 180},
{"CodeCallback_CodeEndGame", 181},
{"CodeCallback_EntityDamage", 182},
{"CodeCallback_EntityOutOfWorld", 183},
{"CodeCallback_HostMigration", 185},
{"CodeCallback_PartyMembers", 187},
{"CodeCallback_PlayerConnect", 188},
{"CodeCallback_PlayerDamage", 189},
{"CodeCallback_PlayerDisconnect", 190},
{"CodeCallback_PlayerGrenadeSuicide", 191},
{"CodeCallback_PlayerKilled", 192},
{"CodeCallback_PlayerLastStand", 193},
{"CodeCallback_PlayerMigrated", 194},
{"CodeCallback_StartGameType", 195},
{"CodeCallback_VehicleDamage", 196},
{"CreateStruct", 221},
{"InitStructs", 522},
{"main", 619},
{"AbortLevel", 1727},
{"callbackVoid", 6662},
{"CodeCallback_GiveKillstreak", 8192},
{"SetDefaultCallbacks", 32577},
{"SetupCallbacks", 33531},
{"SetupDamageFlags", 33542},
{"struct", 36698},
};
}

View File

@ -71,6 +71,18 @@ namespace scripting
}
}
unsigned int find_token_id(const std::string& name)
{
const auto result = token_map.find(name);
if (result != token_map.end())
{
return result->second;
}
return 0;
}
script_function find_function(const std::string& name, const bool prefer_global)
{
const auto index = find_function_index(name, prefer_global);

View File

@ -5,8 +5,10 @@ namespace scripting
{
extern std::unordered_map<std::string, unsigned> method_map;
extern std::unordered_map<std::string, unsigned> function_map;
extern std::unordered_map<std::string, unsigned> token_map;
using script_function = void(*)(game::scr_entref_t);
unsigned int find_token_id(const std::string& name);
script_function find_function(const std::string& name, const bool prefer_global);
}

View File

@ -198,6 +198,12 @@ namespace scripting::lua
return convert(s, entity.get(field));
};
entity_type["getstruct"] = [](const entity& entity, const sol::this_state s)
{
const auto id = entity.get_entity_id();
return scripting::lua::entity_to_struct(s, id);
};
struct game
{
};
@ -328,6 +334,7 @@ namespace scripting::lua
void context::notify(const event& e)
{
this->scheduler_.dispatch(e);
this->event_handler_.dispatch(e);
}

View File

@ -14,6 +14,11 @@ namespace scripting::lua
{
this->remove(handle);
};
event_listener_handle_type["endon"] = [this](const event_listener_handle& handle, const entity& entity, const std::string& event)
{
this->add_endon_condition(handle, entity, event);
};
}
void event_handler::dispatch(const event& event)
@ -24,6 +29,7 @@ namespace scripting::lua
callbacks_.access([&](task_list& tasks)
{
this->merge_callbacks();
this->handle_endon_conditions(event);
for (auto i = tasks.begin(); i != tasks.end();)
{
@ -70,6 +76,27 @@ namespace scripting::lua
return {id};
}
void event_handler::add_endon_condition(const event_listener_handle& handle, const entity& entity,
const std::string& event)
{
auto merger = [&](task_list& tasks)
{
for(auto& task : tasks)
{
if(task.id == handle.id)
{
task.endon_conditions.emplace_back(entity, event);
}
}
};
callbacks_.access([&](task_list& tasks)
{
merger(tasks);
new_callbacks_.access(merger);
});
}
void event_handler::clear()
{
callbacks_.access([&](task_list& tasks)
@ -106,13 +133,33 @@ namespace scripting::lua
{
new_callbacks_.access([&](task_list& new_tasks)
{
tasks.insert(tasks.end(), std::move_iterator<task_list::iterator>(new_tasks.begin()),
std::move_iterator<task_list::iterator>(new_tasks.end()));
tasks.insert(tasks.end(), std::move_iterator(new_tasks.begin()),
std::move_iterator(new_tasks.end()));
new_tasks = {};
});
});
}
void event_handler::handle_endon_conditions(const event& event)
{
auto deleter = [&](task_list& tasks)
{
for(auto& task : tasks)
{
for(auto& condition : task.endon_conditions)
{
if(condition.first == event.entity && condition.second == event.name)
{
task.is_deleted = true;
break;
}
}
}
};
callbacks_.access(deleter);
}
event_arguments event_handler::build_arguments(const event& event) const
{
event_arguments arguments;

View File

@ -19,6 +19,7 @@ namespace scripting::lua
event_callback callback = {};
bool is_volatile = false;
bool is_deleted = false;
std::vector<std::pair<scripting::entity, std::string>> endon_conditions{};
};
class event_handler final
@ -48,6 +49,9 @@ namespace scripting::lua
void remove(const event_listener_handle& handle);
void merge_callbacks();
void handle_endon_conditions(const event& event);
void add_endon_condition(const event_listener_handle& handle, const entity& entity, const std::string& event);
event_arguments build_arguments(const event& event) const;
};

View File

@ -12,6 +12,35 @@ namespace scripting::lua
{
this->remove(handle);
};
task_handle_type["endon"] = [this](const task_handle& handle, const entity& entity, const std::string& event)
{
this->add_endon_condition(handle, entity, event);
};
}
void scheduler::dispatch(const event& event)
{
auto deleter = [&](task_list& tasks)
{
for(auto& task : tasks)
{
for(auto& condition : task.endon_conditions)
{
if(condition.first == event.entity && condition.second == event.name)
{
task.is_deleted = true;
break;
}
}
}
};
callbacks_.access([&](task_list& tasks)
{
deleter(tasks);
new_callbacks_.access(deleter);
});
}
void scheduler::run_frame()
@ -89,6 +118,26 @@ namespace scripting::lua
return {id};
}
void scheduler::add_endon_condition(const task_handle& handle, const entity& entity, const std::string& event)
{
auto merger = [&](task_list& tasks)
{
for(auto& task : tasks)
{
if(task.id == handle.id)
{
task.endon_conditions.emplace_back(entity, event);
}
}
};
callbacks_.access([&](task_list& tasks)
{
merger(tasks);
new_callbacks_.access(merger);
});
}
void scheduler::remove(const task_handle& handle)
{
auto mask_as_deleted = [&](task_list& tasks)

View File

@ -19,6 +19,7 @@ namespace scripting::lua
std::chrono::milliseconds delay{};
bool is_volatile = false;
bool is_deleted = false;
std::vector<std::pair<entity, std::string>> endon_conditions{};
};
class scheduler final
@ -32,6 +33,7 @@ namespace scripting::lua
scheduler(const scheduler&) = delete;
scheduler& operator=(const scheduler&) = delete;
void dispatch(const event& event);
void run_frame();
void clear();
@ -44,6 +46,8 @@ namespace scripting::lua
utils::concurrency::container<task_list, std::recursive_mutex> callbacks_;
std::atomic_int64_t current_task_id_ = 0;
void add_endon_condition(const task_handle& handle, const entity& entity, const std::string& event);
void remove(const task_handle& handle);
void merge_callbacks();
};

View File

@ -1,5 +1,7 @@
#include <std_include.hpp>
#include "value_conversion.hpp"
#include "../functions.hpp"
#include "../execution.hpp"
namespace scripting::lua
{
@ -77,11 +79,16 @@ namespace scripting::lua
return;
}
const auto variable = convert({s, value}).get_raw();
const auto i = values.at(key).index;
const auto variable = &game::scr_VarGlob->childVariableValue[i];
game::scr_VarGlob->childVariableValue[i].type = (char)variable.type;
game::scr_VarGlob->childVariableValue[i].u.u = variable.u;
const auto new_variable = convert({s, value}).get_raw();
game::AddRefToValue(new_variable.type, new_variable.u);
game::RemoveRefToValue(variable->type, variable->u.u);
variable->type = (char)new_variable.type;
variable->u.u = new_variable.u;
};
metatable[sol::meta_function::index] = [values](const sol::table t, const sol::this_state s,
@ -108,6 +115,89 @@ namespace scripting::lua
return {state, table};
}
sol::lua_value convert_function(lua_State* state, const char* pos)
{
return [pos](const entity& entity, const sol::this_state s, sol::variadic_args va)
{
std::vector<script_value> arguments{};
for (auto arg : va)
{
arguments.push_back(convert({s, arg}));
}
return convert(s, scripting::exec_ent_thread(entity, pos, arguments));
};
}
}
sol::lua_value entity_to_struct(lua_State* state, unsigned int parent_id)
{
auto table = sol::table::create(state);
auto metatable = sol::table::create(state);
const auto offset = 64000 * (parent_id & 3);
metatable[sol::meta_function::new_index] = [offset, parent_id](const sol::table t, const sol::this_state s,
const sol::lua_value& field, const sol::lua_value& value)
{
const auto id = field.is<std::string>()
? scripting::find_token_id(field.as<std::string>())
: field.as<int>();
if (!id)
{
return;
}
const auto variable_id = game::GetVariable(parent_id, id);
if (!variable_id)
{
return;
}
const auto variable = &game::scr_VarGlob->childVariableValue[variable_id + offset];
const auto new_variable = convert({s, value}).get_raw();
game::AddRefToValue(new_variable.type, new_variable.u);
game::RemoveRefToValue(variable->type, variable->u.u);
variable->type = (char)new_variable.type;
variable->u.u = new_variable.u;
};
metatable[sol::meta_function::index] = [offset, parent_id](const sol::table t, const sol::this_state s,
const sol::lua_value& field)
{
const auto id = field.is<std::string>()
? scripting::find_token_id(field.as<std::string>())
: field.as<int>();
if (!id)
{
return sol::lua_value{s};
}
const auto variable_id = game::GetVariable(parent_id, id);
if (!variable_id)
{
return sol::lua_value{s};
}
const auto variable = game::scr_VarGlob->childVariableValue[variable_id + offset];
game::VariableValue result{};
result.u = variable.u.u;
result.type = (game::scriptType_e)variable.type;
return convert(s, result);
};
table[sol::metatable_key] = metatable;
return {state, table};
}
script_value convert(const sol::lua_value& value)
@ -172,11 +262,21 @@ namespace scripting::lua
return {state, value.as<std::string>()};
}
if (value.is<std::map<std::string, script_value>>())
{
return entity_to_struct(state, value.get_raw().u.uintValue);
}
if (value.is<std::vector<script_value>>())
{
return entity_to_array(state, value.get_raw().u.uintValue);
}
if (value.is<std::function<void()>>())
{
return convert_function(state, value.get_raw().u.codePosValue);
}
if (value.is<entity>())
{
return {state, value.as<entity>()};

View File

@ -4,6 +4,8 @@
namespace scripting::lua
{
sol::lua_value entity_to_struct(lua_State* state, unsigned int parent_id);
script_value convert(const sol::lua_value& value);
sol::lua_value convert(lua_State* state, const script_value& value);
}

View File

@ -207,6 +207,34 @@ namespace scripting
return type == game::SCRIPT_ARRAY;
}
/***************************************************************
* Struct
**************************************************************/
template <>
bool script_value::is<std::map<std::string, script_value>>() const
{
if (this->get_raw().type != game::SCRIPT_OBJECT)
{
return false;
}
const auto id = this->get_raw().u.uintValue;
const auto type = game::scr_VarGlob->objectVariableValue[id].w.type;
return type == game::SCRIPT_STRUCT;
}
/***************************************************************
* Function
**************************************************************/
template <>
bool script_value::is<std::function<void()>>() const
{
return this->get_raw().type == game::SCRIPT_FUNCTION;
}
/***************************************************************
* Entity
**************************************************************/

View File

@ -1,4 +1,5 @@
#pragma once
#include <d3d11.h>
#define PROTOCOL 1
@ -31,6 +32,8 @@ namespace game
SCRIPT_FLOAT = 5,
SCRIPT_INTEGER = 6,
SCRIPT_END = 8,
SCRIPT_FUNCTION = 9,
SCRIPT_STRUCT = 19,
SCRIPT_ARRAY = 22
};
@ -1224,6 +1227,68 @@ namespace game
const char* buffer;
};
struct GfxImageLoadDef
{
char levelCount;
char numElements;
char pad[2];
int flags;
int format;
int resourceSize;
char data[1];
};
union $3FA29451CE6F1FA138A5ABAB84BE9676
{
ID3D11Texture1D *linemap;
ID3D11Texture2D *map;
ID3D11Texture3D *volmap;
ID3D11Texture2D *cubemap;
GfxImageLoadDef *loadDef;
};
struct GfxTexture
{
$3FA29451CE6F1FA138A5ABAB84BE9676 ___u0;
ID3D11ShaderResourceView *shaderView;
ID3D11ShaderResourceView *shaderViewAlternate;
};
struct Picmip
{
char platform[2];
};
struct CardMemory
{
int platform[2];
};
struct GfxImage
{
GfxTexture textures;
int flags;
int imageFormat;
int resourceSize;
char mapType;
char semantic;
char category;
char flags2;
Picmip picmip;
char track;
//CardMemory cardMemory;
unsigned short width;
unsigned short height;
unsigned short depth;
unsigned short numElements;
char pad3[4];
void* pixelData;
//GfxImageLoadDef *loadDef;
uint64_t streams[4];
const char *name;
};
union XAssetHeader
{
void* data;
@ -1233,6 +1298,7 @@ namespace game
ScriptFile* scriptfile;
StringTable* stringTable;
LuaFile* luaFile;
GfxImage* image;
};
struct XAsset
@ -1267,6 +1333,21 @@ namespace game
namespace mp
{
struct cachedSnapshot_t
{
int archivedFrame;
int time;
int num_entities;
int first_entity;
int num_clients;
int first_client;
int num_agents;
int first_agent;
unsigned int scriptableCount;
unsigned int scriptableFirstIndex;
int usesDelta;
};
struct gclient_s
{
char __pad0[20708];

View File

@ -9,7 +9,10 @@ namespace game
**************************************************************/
WEAK symbol<void(int type, VariableUnion u)> AddRefToValue{0x140315830, 0x1403F1F20};
WEAK symbol<void(unsigned int id)> AddRefToObject{0, 0x1403F1F10};
WEAK symbol<unsigned int(unsigned int id)> AllocThread{0, 0x1403F2270};
WEAK symbol<void(int type, VariableUnion u)> RemoveRefToValue{0x140317340, 0x1403F3A50};
WEAK symbol<void(unsigned int id)> RemoveRefToObject{0, 0x1403F3940};
WEAK symbol<void(void*, void*)> AimAssist_AddToTargetList{0, 0x140001730};
@ -49,10 +52,12 @@ namespace game
WEAK symbol<void(XAssetType type, void (__cdecl* func)(XAssetHeader, void*), void* inData, bool includeOverride)>
DB_EnumXAssets_FastFile{0x14017D7C0, 0x14026EC10};
WEAK symbol<void(XAssetType type, void(__cdecl* func)(game::XAssetHeader, void*), const void* inData, bool includeOverride)>
WEAK symbol<void(XAssetType type, void(__cdecl* func)(XAssetHeader, void*), const void* inData, bool includeOverride)>
DB_EnumXAssets_Internal{0x14017D830, 0x14026EC80};
WEAK symbol<game::XAssetEntry(game::XAssetType type, const char* name)>
WEAK symbol<XAssetEntry(XAssetType type, const char* name)>
DB_FindXAssetEntry{0x14017D830, 0x14026F020};
WEAK symbol<XAssetEntry(XAssetType type, const char *name, int allowCreateDefault)>
DB_FindXAssetHeader{0x14017DCA0, 0x14026F0F0};
WEAK symbol<const char* (const XAsset* asset)> DB_GetXAssetName{0x140151C00, 0x140240DD0};
WEAK symbol<int(XAssetType type)> DB_GetXAssetTypeSize{0x140151C20, 0x140240DF0};
WEAK symbol<void(XZoneInfo* zoneInfo, unsigned int zoneCount, DBSyncMode syncMode)> DB_LoadXAssets{
@ -101,6 +106,7 @@ namespace game
0x14031AAD0, 0x1403F72A0
};
WEAK symbol<unsigned int(unsigned int)> GetObjectType{0x140316F70, 0x1403F3670};
WEAK symbol<unsigned int(unsigned int, unsigned int)> GetVariable{0x0, 0x1403F3730};
WEAK symbol<void()> G_Glass_Update{0x14021D540, 0x1402EDEE0};
@ -115,6 +121,8 @@ namespace game
WEAK symbol<char*(char* string)> I_CleanStr{0x140379010, 0x1404C99A0};
WEAK symbol<char*(GfxImage *image, uint32_t width, uint32_t height, uint32_t depth, uint32_t mipCount, uint32_t imageFlags, DXGI_FORMAT imageFormat, const char *name, const D3D11_SUBRESOURCE_DATA *initData)> Image_Setup{0x1404858D0, 0x1405A3150};
WEAK symbol<const char*(int, int, int)> Key_KeynumToString{0x14013F380, 0x140207C50};
WEAK symbol<unsigned int(int)> Live_SyncOnlineDataFlags{0x1404459A0, 0x140562830};
@ -160,6 +168,8 @@ namespace game
0x14031CB80, 0x1403F92D0
};
WEAK symbol<unsigned int(unsigned int localId, const char* pos, unsigned int paramcount)> VM_Execute{0x0, 0x1403F9E40};
WEAK symbol<const char*(scr_string_t stringValue)> SL_ConvertToString{0x140314850, 0x1403F0F10};
WEAK symbol<scr_string_t(const char* str)> SL_FindString{0x140314AF0, 0x1403F11C0};
WEAK symbol<scr_string_t(const char* str, unsigned int user)> SL_GetString{0x140314D90, 0x1403F1440};

View File

@ -66,4 +66,9 @@ namespace utils
return this->path_;
}
const std::string& binary_resource::get_data() const
{
return this->resource_;
}
}

View File

@ -10,6 +10,7 @@ namespace utils
binary_resource(int id, std::string file);
std::string get_extracted_file();
const std::string& get_data() const;
private:
std::string resource_;

View File

@ -8,6 +8,55 @@ using namespace asmjit::x86;
namespace utils::hook
{
namespace detail
{
template<size_t entries>
std::vector<size_t(*)()> get_iota_functions()
{
if constexpr (entries == 0)
{
std::vector<size_t(*)()> functions;
return functions;
}
else
{
auto functions = get_iota_functions<entries - 1>();
functions.emplace_back([]()
{
return entries - 1;
});
return functions;
}
}
}
// Gets the pointer to the entry in the v-table.
// It seems otherwise impossible to get this.
// This is ugly as fuck and only safely works on x64
// Example:
// ID3D11Device* device = ...
// auto entry = get_vtable_entry(device, &ID3D11Device::CreateTexture2D);
template <size_t entries = 100, typename Class, typename T, typename... Args>
void** get_vtable_entry(Class* obj, T (Class::* entry)(Args ...))
{
union
{
decltype(entry) func;
void* pointer;
};
func = entry;
auto iota_functions = detail::get_iota_functions<entries>();
auto* object = iota_functions.data();
using FakeFunc = size_t(__thiscall*)(void* self);
auto index = static_cast<FakeFunc>(pointer)(&object);
void** obj_v_table = *reinterpret_cast<void***>(obj);
return &obj_v_table[index];
}
class assembler : public Assembler
{
public:
@ -78,7 +127,7 @@ namespace utils::hook
return static_cast<T*>(this->get_original());
}
template <typename T, typename... Args>
template <typename T = void, typename... Args>
T invoke(Args ... args)
{
return static_cast<T(*)(Args ...)>(this->get_original())(args...);

View File

@ -0,0 +1,53 @@
#include "image.hpp"
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
#include <gsl/gsl>
namespace utils
{
image::image(const std::string& image_data)
{
int channels{};
auto* rgb_image = stbi_load_from_memory(reinterpret_cast<const uint8_t*>(image_data.data()), static_cast<int>(image_data.size()), &this->width, &this->height, &channels, 4);
if(!rgb_image)
{
throw std::runtime_error("Unable to load image");
}
auto _ = gsl::finally([rgb_image]()
{
stbi_image_free(rgb_image);
});
const auto size = this->width * this->height * 4;
this->data.resize(size);
std::memmove(this->data.data(), rgb_image, size);
}
int image::get_width() const
{
return this->width;
}
int image::get_height() const
{
return this->height;
}
const void* image::get_buffer() const
{
return this->data.data();
}
size_t image::get_size() const
{
return this->data.size();
}
const std::string& image::get_data() const
{
return this->data;
}
}

View File

@ -0,0 +1,24 @@
#pragma once
#include <string>
namespace utils
{
class image
{
public:
image(const std::string& data);
int get_width() const;
int get_height() const;
const void* get_buffer() const;
size_t get_size() const;
const std::string& get_data() const;
private:
int width{};
int height{};
std::string data{};
};
}

View File

@ -15,7 +15,7 @@ namespace utils::nt
library library::get_by_address(void* address)
{
HMODULE handle = nullptr;
GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, static_cast<LPCSTR>(address), &handle);
GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, static_cast<LPCSTR>(address), &handle);
return library(handle);
}