mirror of
https://github.com/XLabsProject/s1x-client.git
synced 2023-08-02 15:02:12 +02:00
Merge pull request #199 from XLabsProject/release/v1.0.4
Release v1.0.4
This commit is contained in:
commit
9a6089ec56
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -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
|
||||
|
22
CHANGELOG.md
22
CHANGELOG.md
@ -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
2
deps/GSL
vendored
@ -1 +1 @@
|
||||
Subproject commit ef0ffefe525a6219ff245d19a832ce06f3fd3504
|
||||
Subproject commit c1cbb41b428f15e53454682a45f03ea31f1da0a7
|
2
deps/asmjit
vendored
2
deps/asmjit
vendored
@ -1 +1 @@
|
||||
Subproject commit 0dd16b0a98ae1da48563c9cc62f757a9e6bbe9b6
|
||||
Subproject commit 8ee4c76ee3bf4c33478347caefc5d4f7f0e97992
|
19
deps/premake/stb.lua
vendored
Normal file
19
deps/premake/stb.lua
vendored
Normal 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
2
deps/rapidjson
vendored
@ -1 +1 @@
|
||||
Subproject commit e0f68a435610e70ab5af44fc6a90523d69b210b3
|
||||
Subproject commit 17aa824c928ea111e9b12a61e06d98335ce98f15
|
2
deps/sol2
vendored
2
deps/sol2
vendored
@ -1 +1 @@
|
||||
Subproject commit f56b3c698c2116f41aad3bf731ba8400e68c498b
|
||||
Subproject commit 430b55a49609daacea6fd3ee2c9d137db4db9c83
|
1
deps/stb
vendored
Submodule
1
deps/stb
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit c9064e317699d2e495f36ba4f9ac037e88ee371a
|
@ -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
|
||||
|
@ -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;
|
||||
|
135
src/client/component/images.cpp
Normal file
135
src/client/component/images.cpp
Normal 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)
|
6
src/client/component/images.hpp
Normal file
6
src/client/component/images.hpp
Normal file
@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
namespace images
|
||||
{
|
||||
void override_texture(std::string name, std::string data);
|
||||
}
|
@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -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))
|
||||
{
|
||||
|
@ -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)
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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},
|
||||
};
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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)
|
||||
|
@ -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();
|
||||
};
|
||||
|
@ -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>()};
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
**************************************************************/
|
||||
|
@ -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];
|
||||
|
@ -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};
|
||||
|
@ -66,4 +66,9 @@ namespace utils
|
||||
|
||||
return this->path_;
|
||||
}
|
||||
|
||||
const std::string& binary_resource::get_data() const
|
||||
{
|
||||
return this->resource_;
|
||||
}
|
||||
}
|
||||
|
@ -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_;
|
||||
|
@ -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...);
|
||||
|
53
src/common/utils/image.cpp
Normal file
53
src/common/utils/image.cpp
Normal 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;
|
||||
}
|
||||
}
|
24
src/common/utils/image.hpp
Normal file
24
src/common/utils/image.hpp
Normal 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{};
|
||||
};
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user