Merge branch 'fedddddd:develop' into locale

This commit is contained in:
Vlad 2022-09-02 03:19:55 +03:00 committed by GitHub
commit ec90d34634
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 520 additions and 2410 deletions

2
deps/gsc-tool-h2 vendored

@ -1 +1 @@
Subproject commit b18d79c1da68c8bd97bbba91b72321a1302e651f
Subproject commit 77170a40a6558bb6f54c6d796f1574ba2de497a3

View File

@ -248,7 +248,7 @@ namespace command
game::CG_GameMessage(0, utils::string::va("godmode %s",
game::g_entities[0].flags & game::FL_GODMODE
? "^2on"
: "^1off"));
: "^1off"), 0);
});
add("demigod", []()
@ -262,7 +262,7 @@ namespace command
game::CG_GameMessage(0, utils::string::va("demigod mode %s",
game::g_entities[0].flags & game::FL_DEMI_GODMODE
? "^2on"
: "^1off"));
: "^1off"), 0);
});
add("notarget", []()
@ -276,7 +276,7 @@ namespace command
game::CG_GameMessage(0, utils::string::va("notarget %s",
game::g_entities[0].flags & game::FL_NOTARGET
? "^2on"
: "^1off"));
: "^1off"), 0);
});
add("noclip", []()
@ -290,7 +290,7 @@ namespace command
game::CG_GameMessage(0, utils::string::va("noclip %s",
game::g_entities[0].client->flags & 1
? "^2on"
: "^1off"));
: "^1off"), 0);
});
add("ufo", []()
@ -302,7 +302,7 @@ namespace command
game::g_entities[0].client->flags ^= 2;
game::CG_GameMessage(
0, utils::string::va("ufo %s", game::g_entities[0].client->flags & 2 ? "^2on" : "^1off"));
0, utils::string::va("ufo %s", game::g_entities[0].client->flags & 2 ? "^2on" : "^1off"), 0);
});
add("give", [](const params& params)
@ -314,7 +314,7 @@ namespace command
if (params.size() < 2)
{
game::CG_GameMessage(0, "You did not specify a weapon name");
game::CG_GameMessage(0, "You did not specify a weapon name", 0);
return;
}
@ -379,7 +379,7 @@ namespace command
}
else
{
game::CG_GameMessage(0, "Weapon does not exist");
game::CG_GameMessage(0, "Weapon does not exist", 0);
}
}
}
@ -419,7 +419,7 @@ namespace command
if (params.size() < 2)
{
game::CG_GameMessage(0, "You did not specify a weapon name");
game::CG_GameMessage(0, "You did not specify a weapon name", 0);
return;
}

View File

@ -17,6 +17,7 @@ namespace discord
DiscordRichPresence discord_presence;
std::string state;
std::optional<std::string> details{};
std::optional<std::string> image{};
void update_discord()
{
@ -26,6 +27,7 @@ namespace discord
{
state = {};
details.reset();
image.reset();
discord_presence.details = game::UI_SafeTranslateString("MENU_MAIN_MENU");
discord_presence.state = "";
@ -37,7 +39,17 @@ namespace discord
}
else
{
const auto map = game::Dvar_FindVar("mapname")->current.string;
static char map[0x1000] = {0};
if (image.has_value())
{
strncpy_s(map, image.value().data(), sizeof(map));
}
else
{
const auto mapname = game::Dvar_FindVar("mapname")->current.string;
strncpy_s(map, mapname, sizeof(map));
}
const auto mapname = game::UI_SafeTranslateString(utils::string::va("PRESENCE_SP_%s", map));
discord_presence.largeImageKey = map;
@ -112,6 +124,16 @@ namespace discord
update_discord();
}, scheduler::pipeline::async);
});
command::add("setdiscordimage", [](const command::params& params)
{
const std::string image_ = params.join(1);
scheduler::once([=]()
{
image = image_;
update_discord();
}, scheduler::pipeline::async);
});
}
private:

View File

@ -319,6 +319,7 @@ namespace fastfiles
reallocate_asset_pool_multiplier<game::ASSET_TYPE_SOUND, 2>();
reallocate_asset_pool_multiplier<game::ASSET_TYPE_LOADED_SOUND, 2>();
reallocate_asset_pool_multiplier<game::ASSET_TYPE_XANIM, 2>();
reallocate_asset_pool_multiplier<game::ASSET_TYPE_LOCALIZE, 2>();
}
void add_custom_level_load_zone(void* load, const char* name, bool localized, const size_t size_est)

View File

@ -11,6 +11,7 @@
#include <utils/io.hpp>
#include <utils/hook.hpp>
#include <utils/flags.hpp>
#include <utils/properties.hpp>
namespace filesystem
{
@ -44,7 +45,7 @@ namespace filesystem
initialized = true;
filesystem::register_path(L"" CLIENT_DATA_FOLDER);
filesystem::register_path(utils::properties::get_appdata_path() / CLIENT_DATA_FOLDER);
filesystem::register_path(L".");
filesystem::register_path(L"h2-mod");

View File

@ -9,6 +9,7 @@
#include "scripting.hpp"
#include "gsc.hpp"
#include "scheduler.hpp"
#include "fastfiles.hpp"
#include "game/scripting/execution.hpp"
#include "game/scripting/functions.hpp"
@ -20,13 +21,11 @@
#include <xsk/gsc/interfaces/compiler.hpp>
#include <xsk/gsc/interfaces/assembler.hpp>
#include <xsk/resolver.hpp>
#include <xsk/utils/compression.hpp>
#include <interface.hpp>
#include <utils/hook.hpp>
#include <utils/io.hpp>
#include <utils/string.hpp>
#include <utils/compression.hpp>
namespace gsc
{
@ -42,7 +41,7 @@ namespace gsc
std::unordered_map<std::string, unsigned int> main_handles;
std::unordered_map<std::string, unsigned int> init_handles;
std::unordered_map<std::string, game::ScriptFile*> loaded_scripts;
std::unordered_map<unsigned int, scripting::script_function> functions;
std::unordered_map<scripting::script_function, unsigned int> functions;
std::optional<std::string> gsc_error;
char* allocate_buffer(size_t size)
@ -50,6 +49,32 @@ namespace gsc
return utils::hook::invoke<char*>(0x14061E680, size, 4, 1, 5);
}
bool read_scriptfile(const std::string& name, std::string* data)
{
if (filesystem::read_file(name, data))
{
return true;
}
const auto name_str = name.data();
if (game::DB_XAssetExists(game::ASSET_TYPE_RAWFILE, name_str) &&
!game::DB_IsXAssetDefault(game::ASSET_TYPE_RAWFILE, name_str))
{
const auto asset = game::DB_FindXAssetHeader(game::ASSET_TYPE_RAWFILE, name.data(), false);
const auto len = game::DB_GetRawFileLen(asset.rawfile);
data->resize(len);
game::DB_GetRawBuffer(asset.rawfile, data->data(), len);
if (len > 0)
{
data->pop_back();
}
return true;
}
return false;
}
game::ScriptFile* load_custom_script(const char* file_name, const std::string& real_name)
{
if (loaded_scripts.find(real_name) != loaded_scripts.end())
@ -58,7 +83,7 @@ namespace gsc
}
std::string source_buffer{};
if (!filesystem::read_file(real_name + ".gsc", &source_buffer))
if (!read_scriptfile(real_name + ".gsc", &source_buffer))
{
return nullptr;
}
@ -90,23 +115,47 @@ namespace gsc
const auto script = assembler->output_script();
script_file_ptr->bytecodeLen = static_cast<int>(script.size());
const auto compressed = xsk::utils::zlib::compress(stack);
const auto buffer_size = script.size() + compressed.size() + 1;
const auto script_size = script.size();
const auto buffer_size = script_size + stack.size() + 2;
const auto buffer = allocate_buffer(buffer_size);
std::memcpy(buffer, script.data(), script_size);
std::memcpy(&buffer[script_size], compressed.data(), compressed.size());
std::memcpy(&buffer[script_size], stack.data(), stack.size());
script_file_ptr->bytecode = &buffer[0];
script_file_ptr->buffer = &buffer[script.size()];
script_file_ptr->compressedLen = static_cast<int>(compressed.size());
script_file_ptr->compressedLen = 0;
loaded_scripts[real_name] = script_file_ptr;
return script_file_ptr;
}
void load_script(const std::string& name)
{
if (!game::Scr_LoadScript(name.data()))
{
return;
}
const auto main_handle = game::Scr_GetFunctionHandle(name.data(),
xsk::gsc::h2::resolver::token_id("main"));
const auto init_handle = game::Scr_GetFunctionHandle(name.data(),
xsk::gsc::h2::resolver::token_id("init"));
if (main_handle)
{
console::info("Loaded '%s::main'\n", name.data());
main_handles[name] = main_handle;
}
if (init_handle)
{
console::info("Loaded '%s::init'\n", name.data());
init_handles[name] = init_handle;
}
}
void load_scripts(const std::filesystem::path& root_dir)
{
std::filesystem::path script_dir = root_dir / "scripts";
@ -127,26 +176,7 @@ namespace gsc
const auto relative = path.lexically_relative(root_dir).generic_string();
const auto base_name = relative.substr(0, relative.size() - 4);
if (!game::Scr_LoadScript(base_name.data()))
{
continue;
}
const auto main_handle = game::Scr_GetFunctionHandle(base_name.data(),
xsk::gsc::h2::resolver::token_id("main"));
const auto init_handle = game::Scr_GetFunctionHandle(base_name.data(),
xsk::gsc::h2::resolver::token_id("init"));
if (main_handle)
{
console::info("Loaded '%s::main'\n", base_name.data());
main_handles[base_name] = main_handle;
}
else if (init_handle)
{
console::info("Loaded '%s::init'\n", base_name.data());
init_handles[base_name] = init_handle;
}
load_script(base_name);
}
}
@ -157,27 +187,23 @@ namespace gsc
loaded_scripts.clear();
}
void gscr_print_stub()
{
const auto num = game::Scr_GetNumParam();
std::string buffer{};
for (auto i = 0; i < num; i++)
{
const auto str = game::Scr_GetString(i);
buffer.append(str);
buffer.append("\t");
}
printf("%s\n", buffer.data());
}
void load_gametype_script_stub(void* a1, void* a2)
{
utils::hook::invoke<void>(0x1404E1400, a1, a2);
clear();
fastfiles::enum_assets(game::ASSET_TYPE_RAWFILE, [](game::XAssetHeader header)
{
std::string name = header.scriptfile->name;
if (name.ends_with(".gsc") && name.starts_with("scripts/"))
{
const auto base_name = name.substr(0, name.size() - 4);
load_script(base_name);
}
}, true);
for (const auto& path : filesystem::get_search_paths())
{
load_scripts(path);
@ -218,6 +244,18 @@ namespace gsc
return utils::hook::invoke<int>(0x1404143C0, type, name);
}
void db_get_raw_buffer_stub(const game::RawFile* rawfile, char* buf, const int size)
{
if (rawfile->len > 0 && rawfile->compressedLen == 0)
{
std::memset(buf, 0, size);
std::memcpy(buf, rawfile->buffer, std::min(rawfile->len, size));
return;
}
utils::hook::invoke<void>(0x140413C40, rawfile, buf, size);
}
std::optional<std::pair<std::string, std::string>> find_function(const char* pos)
{
for (const auto& file : scripting::script_function_table_sort)
@ -239,15 +277,19 @@ namespace gsc
{
for (auto frame = game::scr_VmPub->function_frame; frame != game::scr_VmPub->function_frame_start; --frame)
{
const auto pos = frame == game::scr_VmPub->function_frame
? game::scr_function_stack->pos
: frame->fs.pos;
const auto function = find_function(frame->fs.pos);
if (function.has_value())
{
console::warn("\tat function \"%s\" in file \"%s.gsc\"",
function.value().first.data(), function.value().second.data(), frame->fs.pos);
function.value().first.data(), function.value().second.data());
}
else
{
console::warn("\tat unknown location", frame->fs.pos);
console::warn("\tat unknown location %p", pos);
}
}
}
@ -267,13 +309,11 @@ namespace gsc
bool force_error_print = false;
void* vm_error_stub(void* a1)
{
if (!developer_script->current.enabled || force_error_print)
if (!developer_script->current.enabled && !force_error_print)
{
return utils::hook::invoke<void*>(0x140614670, a1);
}
force_error_print = false;
console::warn("*********** script runtime error *************\n");
const auto opcode_id = *reinterpret_cast<std::uint8_t*>(0x14BAA93E8);
@ -292,15 +332,78 @@ namespace gsc
opcode_id, error_str.data());
}
force_error_print = false;
gsc_error = {};
print_callstack();
console::warn("**********************************************\n");
return utils::hook::invoke<void*>(0x140614670, a1);
}
void unknown_function_stub()
std::string unknown_function_error{};
void get_unknown_function_error(const char* code_pos)
{
game::Com_Error(game::ERR_DROP, "LinkFile: unknown function in script '%s.gsc'",
scripting::current_file.data());
const auto function = find_function(code_pos);
if (function.has_value())
{
const auto& pos = function.value();
unknown_function_error = utils::string::va(
"while processing function '%s' in script '%s':\nunknown script '%s'",
pos.first.data(), pos.second.data(), scripting::current_file.data()
);
}
else
{
unknown_function_error = utils::string::va(
"unknown script '%s'",
scripting::current_file.data()
);
}
}
unsigned int current_filename{};
std::string get_filename_name()
{
const auto filename_str = game::SL_ConvertToString(
static_cast<game::scr_string_t>(current_filename));
const auto id = std::atoi(filename_str);
if (id == 0)
{
return filename_str;
}
return scripting::get_token_single(id);
}
void get_unknown_function_error(unsigned int thread_name)
{
const auto filename = get_filename_name();
const auto name = scripting::get_token_single(thread_name);
unknown_function_error = utils::string::va(
"while processing script '%s':\nunknown function '%s::%s'",
scripting::current_file.data(), filename.data(), name.data()
);
}
void unknown_function_stub(const char* code_pos)
{
get_unknown_function_error(code_pos);
game::Com_Error(game::ERR_DROP, "script link error\n%s",
unknown_function_error.data());
}
unsigned int find_variable_stub(unsigned int parent_id, unsigned int thread_name)
{
const auto res = game::FindVariable(parent_id, thread_name);
if (!res)
{
get_unknown_function_error(thread_name);
game::Com_Error(game::ERR_DROP, "script link error\n%s",
unknown_function_error.data());
}
return res;
}
void register_gsc_functions_stub(void* a1, void* a2)
@ -308,7 +411,7 @@ namespace gsc
utils::hook::invoke<void>(0x140509F20, a1, a2);
for (const auto& func : functions)
{
game::Scr_RegisterFunction(func.second, 0, func.first);
game::Scr_RegisterFunction(func.first, 0, func.second);
}
}
@ -319,24 +422,68 @@ namespace gsc
return {};
}
const auto value = &game::scr_VmPub->top[-index];
return scripting::script_value(*value);
return game::scr_VmPub->top[-index];
}
auto function_id_start = 0x320;
void add_function(const std::string& name, scripting::script_function function)
{
const auto id = ++function_id_start;
scripting::function_map[name] = id;
functions[id] = function;
if (xsk::gsc::h2::resolver::find_function(name))
{
const auto id = xsk::gsc::h2::resolver::function_id(name);
functions[function] = id;
}
else
{
const auto id = ++function_id_start;
xsk::gsc::h2::resolver::add_function(name, static_cast<std::uint16_t>(id));
functions[function] = id;
}
}
void set_gsc_error(const std::string& error)
void execute_custom_function(scripting::script_function function)
{
force_error_print = true;
gsc_error = error;
game::Scr_ErrorInternal();
auto error = false;
try
{
function({});
}
catch (const std::exception& e)
{
error = true;
force_error_print = true;
gsc_error = e.what();
}
if (error)
{
game::Scr_ErrorInternal();
}
}
void vm_call_builtin_stub(scripting::script_function function)
{
auto custom = false;
{
custom = functions.find(function) != functions.end();
}
if (!custom)
{
function({});
}
else
{
execute_custom_function(function);
}
}
utils::hook::detour scr_emit_function_hook;
void scr_emit_function_stub(unsigned int filename, unsigned int thread_name, char* code_pos)
{
current_filename = filename;
scr_emit_function_hook.invoke<void>(filename, thread_name, code_pos);
}
}
@ -365,28 +512,12 @@ namespace gsc
{
developer_script = dvars::register_bool("developer_script", false, 0, "Print GSC errors");
// wait for other tokens to be added
scheduler::once([]()
{
for (const auto& function : scripting::function_map)
{
xsk::gsc::h2::resolver::add_function(function.first, static_cast<std::uint16_t>(function.second));
}
for (const auto& method : scripting::method_map)
{
xsk::gsc::h2::resolver::add_method(method.first, static_cast<std::uint16_t>(method.second));
}
for (const auto& token : scripting::token_map)
{
xsk::gsc::h2::resolver::add_token(token.first, static_cast<std::uint16_t>(token.second));
}
}, scheduler::pipeline::main);
utils::hook::call(0x1405C6177, find_script);
utils::hook::call(0x1405C6187, db_is_xasset_default);
// Loads scripts with an uncompressed stack
utils::hook::call(0x1405C61E0, db_get_raw_buffer_stub);
// load handles
utils::hook::call(0x1404E17B2, load_gametype_script_stub);
@ -394,13 +525,12 @@ namespace gsc
utils::hook::call(0x1404C8F71, g_load_structs_stub);
utils::hook::call(0x1404C8F80, scr_load_level_stub);
// replace builtin print function
utils::hook::jump(0x1404EC640, gscr_print_stub);
utils::hook::call(0x1405CB94F, vm_error_stub);
utils::hook::call(0x1405BC583, unknown_function_stub);
utils::hook::call(0x1405BC5CF, unknown_function_stub);
utils::hook::call(0x1405BC6BA, find_variable_stub);
scr_emit_function_hook.create(0x1405BC5E0, scr_emit_function_stub);
utils::hook::call(0x1405BCBAB, register_gsc_functions_stub);
utils::hook::set<uint32_t>(0x1405BC7BC, 0x1000); // change builtin func count
@ -411,31 +541,77 @@ namespace gsc
utils::hook::inject(0x1405BCB78 + 3, &func_table);
utils::hook::set<uint32_t>(0x1405CA678 + 4, RVA(&func_table));
utils::hook::nop(0x1405CA683, 8);
utils::hook::call(0x1405CA683, vm_call_builtin_stub);
add_function("print", [](const game::scr_entref_t ref)
{
const auto num = game::Scr_GetNumParam();
std::string buffer{};
for (auto i = 0; i < num; i++)
{
const auto str = game::Scr_GetString(i);
buffer.append(str);
buffer.append("\t");
}
printf("%s\n", buffer.data());
});
add_function("assert", [](const game::scr_entref_t ref)
{
const auto expr = get_argument(0).as<int>();
if (!expr)
{
throw std::runtime_error("assert fail");
}
});
add_function("assertex", [](const game::scr_entref_t ref)
{
const auto expr = get_argument(0).as<int>();
if (!expr)
{
const auto error = get_argument(1).as<std::string>();
throw std::runtime_error(error);
}
});
add_function("replacefunc", [](const game::scr_entref_t ref)
{
try
const auto what = get_argument(0).get_raw();
const auto with = get_argument(1).get_raw();
if (what.type != game::SCRIPT_FUNCTION)
{
const auto what = get_argument(0).get_raw();
const auto with = get_argument(1).get_raw();
if (what.type != game::SCRIPT_FUNCTION)
{
set_gsc_error("replaceFunc: parameter 0 must be a function");
return;
}
if (with.type != game::SCRIPT_FUNCTION)
{
set_gsc_error("replaceFunc: parameter 0 must be a function");
return;
}
notifies::set_gsc_hook(what.u.codePosValue, with.u.codePosValue);
throw std::runtime_error("replaceFunc: parameter 1 must be a function");
}
catch (const std::exception& e)
if (with.type != game::SCRIPT_FUNCTION)
{
set_gsc_error(utils::string::va("replaceFunc: %s", e.what()));
throw std::runtime_error("replaceFunc: parameter 2 must be a function");
}
notifies::set_gsc_hook(what.u.codePosValue, with.u.codePosValue);
});
add_function("getsoundlength", [](const game::scr_entref_t ref)
{
const auto name = get_argument(0);
if (!name.is<std::string>())
{
throw std::runtime_error("getsoundlength: parameter 1 must be a string");
}
const auto name_str = name.as<std::string>();
const auto sound = game::DB_FindXAssetHeader(game::ASSET_TYPE_SOUND, name_str.data(), false).sound;
if (!sound || !sound->count || !sound->head->soundFile || sound->head->soundFile->type != game::SAT_STREAMED)
{
return game::Scr_AddInt(-1);
}
return game::Scr_AddInt(sound->head->soundFile->u.streamSnd.totalMsec);
});
scripting::on_shutdown([](int free_scripts)

View File

@ -11,6 +11,10 @@
#include "command.hpp"
#include "game/scripting/functions.hpp"
#include <xsk/gsc/types.hpp>
#include <xsk/resolver.hpp>
#include <xsk/utils/compression.hpp>
#include <utils/hook.hpp>
#include <utils/concurrency.hpp>
#include <utils/string.hpp>
@ -169,13 +173,14 @@ namespace mapents
}
const auto key_ = key.substr(1, key.size() - 2);
if (scripting::token_map.find(key_) == scripting::token_map.end())
const auto id = xsk::gsc::h2::resolver::token_id(key_);
if (id == 0)
{
console::warn("[addon_map_ents parser] Key '%s' not found, on line %i", key_.data(), line_index);
continue;
}
out_buffer.append(utils::string::va("%i \"%s\"\n", scripting::token_map[key_], value.data()));
out_buffer.append(utils::string::va("%i \"%s\"\n", id, value.data()));
}
return out_buffer;
@ -243,7 +248,6 @@ namespace mapents
{
if (!should_load_addon_mapents())
{
printf("db_find_xasset_header_stub %s\n", name);
return game::DB_FindXAssetHeader(type, name, allow_create_default);
}
@ -289,13 +293,13 @@ namespace mapents
{
const auto id = token_id_start++;
custom_fields[id] = type;
scripting::token_map[name] = id;
xsk::gsc::h2::resolver::add_token(name, static_cast<std::uint16_t>(id));
}
void add_field(const std::string& name, game::scriptType_e type, unsigned int id)
{
custom_fields[id] = type;
scripting::token_map[name] = id;
xsk::gsc::h2::resolver::add_token(name, static_cast<std::uint16_t>(id));
}
utils::hook::detour scr_find_field_hook;
@ -335,15 +339,8 @@ namespace mapents
}
const auto id = static_cast<unsigned int>(std::atoi(line.substr(0, first_space).data()));
std::string key = std::to_string(id);
for (const auto& [token, value] : scripting::token_map)
{
if (value == id)
{
key = "\"" + token + "\"";
break;
}
}
const auto token = xsk::gsc::h2::resolver::token_name(static_cast<std::uint16_t>(id));
const auto key = "\"" + token + "\"";
const auto new_line = key + line.substr(first_space);
buffer.append(new_line);

View File

@ -93,7 +93,7 @@ namespace mods
if (!game::Com_InFrontend())
{
console::error("Cannot load mod while in-game!\n");
game::CG_GameMessage(0, "^1Cannot unload mod while in-game!");
game::CG_GameMessage(0, "^1Cannot unload mod while in-game!", 0);
return;
}
@ -132,7 +132,7 @@ namespace mods
if (!game::Com_InFrontend())
{
console::error("Cannot unload mod while in-game!\n");
game::CG_GameMessage(0, "^1Cannot unload mod while in-game!");
game::CG_GameMessage(0, "^1Cannot unload mod while in-game!", 0);
return;
}

View File

@ -152,7 +152,7 @@ namespace notifies
}
void scr_entity_damage_stub(game::gentity_s* self, game::gentity_s* inflictor, game::gentity_s* attacker, const float* vDir, const float* vPoint,
int damage, int dflags, const unsigned int hitLoc, const unsigned int weapon, bool isAlternate, unsigned int a11, const int meansOfDeath, unsigned int a13, unsigned int a14)
int damage, int dflags, const unsigned int meansOfDeath, const unsigned int weapon, bool isAlternate, unsigned int a11, const int hitLoc, unsigned int a13, unsigned int a14)
{
{
const std::string _hitLoc = reinterpret_cast<const char**>(0x140BF4AA0)[hitLoc];
@ -187,7 +187,8 @@ namespace notifies
}
}
scr_entity_damage_hook.invoke<void>(self, inflictor, attacker, vDir, vPoint, damage, dflags, hitLoc, weapon, isAlternate, a11, meansOfDeath, a13, a14);
scr_entity_damage_hook.invoke<void>(self,inflictor, attacker, vDir, vPoint, damage, dflags,
meansOfDeath, weapon, isAlternate, a11, hitLoc, a13, a14);
}
}
@ -233,8 +234,7 @@ namespace notifies
public:
void post_unpack() override
{
const auto a = utils::hook::assemble(vm_execute_stub);
utils::hook::jump(0x1405C90A5, a, true);
utils::hook::jump(0x1405C90A5, utils::hook::assemble(vm_execute_stub), true);
scr_entity_damage_hook.create(0x1404BD2E0, scr_entity_damage_stub);
}

View File

@ -165,22 +165,12 @@ namespace scripting
return result;
}
std::string get_token_single(unsigned int id)
{
if (canonical_string_table.find(id) != canonical_string_table.end())
{
return canonical_string_table[id];
}
return scripting::find_token_single(id);
}
void add_function_sort(unsigned int id, const char* pos)
{
std::string filename = current_file;
if (current_file_id)
{
filename = get_token_single(current_file_id);
filename = scripting::get_token_single(current_file_id);
}
if (script_function_table_sort.find(filename) == script_function_table_sort.end())
@ -289,6 +279,16 @@ namespace scripting
}
}
std::string get_token_single(unsigned int id)
{
if (canonical_string_table.find(id) != canonical_string_table.end())
{
return canonical_string_table[id];
}
return scripting::find_token_single(id);
}
void on_shutdown(const std::function<void(bool)>& callback)
{
shutdown_callbacks.push_back(callback);

View File

@ -16,4 +16,5 @@ namespace scripting
void on_shutdown(const std::function<void(bool)>& callback);
std::optional<std::string> get_canonical_string(const unsigned int id);
std::string get_token_single(unsigned int id);
}

View File

@ -444,25 +444,28 @@ namespace ui_scripting
reader_data, chunk_name);
}
}
std::string current_error;
int main_handler(game::hks::lua_State* state)
{
const auto value = state->m_apistack.base[-1];
if (value.t != game::hks::TCFUNCTION)
{
return 0;
}
const auto closure = value.v.cClosure;
if (converted_functions.find(closure) == converted_functions.end())
{
return 0;
}
const auto& function = converted_functions[closure];
bool error = false;
try
{
const auto value = state->m_apistack.base[-1];
if (value.t != game::hks::TCFUNCTION)
{
return 0;
}
const auto closure = value.v.cClosure;
if (converted_functions.find(closure) == converted_functions.end())
{
return 0;
}
const auto& function = converted_functions[closure];
const auto args = get_return_values();
const auto results = function(args);
@ -475,7 +478,13 @@ namespace ui_scripting
}
catch (const std::exception& e)
{
game::hks::hksi_luaL_error(state, e.what());
current_error = e.what();
error = true;
}
if (error)
{
game::hks::hksi_luaL_error(state, current_error.data());
}
return 0;

View File

@ -19,6 +19,7 @@
#include <utils/cryptography.hpp>
#include <utils/io.hpp>
#include <utils/string.hpp>
#include <utils/properties.hpp>
#define MASTER "https://master.fed0001.xyz/"
@ -63,17 +64,14 @@ namespace updater
std::vector<std::string> garbage_files{};
};
utils::concurrency::container<update_data_t> update_data;
// remove this at some point
std::vector<std::string> old_data_files =
{
{"./data/ui_scripts"},
{"./data/polrus"},
{"./data/fonts"},
{"./data/localizedstrings"},
{"./cdata"},
};
utils::concurrency::container<update_data_t> update_data;
std::string select(const std::string& main, const std::string& develop)
{
if (GIT_BRANCH == "develop"s)
@ -84,6 +82,18 @@ namespace updater
return main;
}
std::string load_binary_name()
{
utils::nt::library self;
return self.get_name();
}
std::string get_binary_name()
{
static const auto name = load_binary_name();
return name;
}
void notify(const std::string& name)
{
scheduler::once([=]()
@ -118,9 +128,22 @@ namespace updater
bool check_file(const std::string& name, const std::string& sha)
{
std::string data;
if (!utils::io::read_file(name, &data))
if (get_binary_name() == name)
{
return false;
if (!utils::io::read_file(name, &data))
{
return false;
}
}
else
{
const auto appdata_folder = utils::properties::get_appdata_path();
const auto path = (appdata_folder / name).generic_string();
if (!utils::io::read_file(path, &data))
{
return false;
}
}
if (utils::cryptography::sha1::compute(data, true) != sha)
@ -131,18 +154,6 @@ namespace updater
return true;
}
std::string load_binary_name()
{
utils::nt::library self;
return self.get_name();
}
std::string get_binary_name()
{
static const auto name = load_binary_name();
return name;
}
std::string get_time_str()
{
return utils::string::va("%i", uint32_t(time(nullptr)));
@ -153,39 +164,6 @@ namespace updater
return utils::http::get_data(MASTER + select(DATA_PATH, DATA_PATH_DEV) + name + "?" + get_time_str());
}
bool is_update_cancelled()
{
return update_data.access<bool>([](update_data_t& data_)
{
return data_.cancelled;
});
}
bool write_file(const std::string& name, const std::string& data)
{
if (get_binary_name() == name &&
utils::io::file_exists(name) &&
!utils::io::move_file(name, name + ".old"))
{
return false;
}
return utils::io::write_file(name, data);
}
void delete_old_file()
{
utils::io::remove_file(get_binary_name() + ".old");
}
void reset_data()
{
update_data.access([](update_data_t& data_)
{
data_ = {};
});
}
bool has_old_data_files()
{
bool has = false;
@ -208,23 +186,68 @@ namespace updater
}
}
bool is_update_cancelled()
{
return update_data.access<bool>([](update_data_t& data_)
{
return data_.cancelled;
});
}
bool write_file(const std::string& name, const std::string& data)
{
if (get_binary_name() == name &&
utils::io::file_exists(name) &&
!utils::io::move_file(name, name + ".old"))
{
return false;
}
if (get_binary_name() == name)
{
return utils::io::write_file(name, data);
}
else
{
const auto appdata_folder = utils::properties::get_appdata_path();
const auto path = (appdata_folder / name).generic_string();
return utils::io::write_file(path, data);
}
}
void delete_old_file()
{
utils::io::remove_file(get_binary_name() + ".old");
}
void reset_data()
{
update_data.access([](update_data_t& data_)
{
data_ = {};
});
}
std::vector<std::string> find_garbage_files(const std::vector<std::string>& update_files)
{
std::vector<std::string> garbage_files{};
if (!utils::io::directory_exists("cdata"))
const auto appdata_folder = utils::properties::get_appdata_path();
const auto path = (appdata_folder / CLIENT_DATA_FOLDER).generic_string();
if (!utils::io::directory_exists(path))
{
return {};
}
const auto current_files = utils::io::list_files_recursively(CLIENT_DATA_FOLDER);
const auto current_files = utils::io::list_files_recursively(path);
for (const auto& file : current_files)
{
bool found = false;
for (const auto& update_file : update_files)
{
const auto update_file_ = (appdata_folder / update_file).generic_string();
const auto path_a = std::filesystem::path(file);
const auto path_b = std::filesystem::path(update_file);
const auto path_b = std::filesystem::path(update_file_);
const auto is_directory = utils::io::directory_exists(file);
const auto compare = path_a.compare(path_b);

File diff suppressed because it is too large Load Diff

View File

@ -3,57 +3,36 @@
#include "../../component/gsc.hpp"
#include <xsk/gsc/types.hpp>
#include <xsk/resolver.hpp>
#include <xsk/utils/compression.hpp>
#include <utils/string.hpp>
namespace scripting
{
namespace
{
std::unordered_map<std::string, unsigned> lowercase_map(
const std::unordered_map<std::string, unsigned>& old_map)
{
std::unordered_map<std::string, unsigned> new_map{};
for (auto& entry : old_map)
{
new_map[utils::string::to_lower(entry.first)] = entry.second;
}
return new_map;
}
const std::unordered_map<std::string, unsigned>& get_methods()
{
static auto methods = lowercase_map(method_map);
return methods;
}
const std::unordered_map<std::string, unsigned>& get_functions()
{
static auto function = lowercase_map(function_map);
return function;
}
int find_function_index(const std::string& name, const bool prefer_global)
{
const auto target = utils::string::to_lower(name);
const auto& primary_map = prefer_global
? get_functions()
: get_methods();
const auto& secondary_map = !prefer_global
? get_functions()
: get_methods();
auto function_entry = primary_map.find(target);
if (function_entry != primary_map.end())
auto first = xsk::gsc::h2::resolver::function_id;
auto second = xsk::gsc::h2::resolver::method_id;
if (!prefer_global)
{
return function_entry->second;
std::swap(first, second);
}
function_entry = secondary_map.find(target);
if (function_entry != secondary_map.end())
const auto first_res = first(target);
if (first_res)
{
return function_entry->second;
return first_res;
}
const auto second_res = second(target);
if (second_res)
{
return second_res;
}
return -1;
@ -64,7 +43,7 @@ namespace scripting
static const auto function_table = &gsc::func_table;
static const auto method_table = 0x14B155890;
if (index < 0x320)
if (index < 0x1000)
{
return reinterpret_cast<script_function*>(function_table)[index - 1];
}
@ -79,11 +58,6 @@ namespace scripting
return static_cast<unsigned int>(std::strtol(name.substr(3).data(), nullptr, 10));
}
if (name.starts_with("_id_"))
{
return static_cast<unsigned int>(std::strtol(name.substr(4).data(), nullptr, 16));
}
return 0;
}
}
@ -92,43 +66,24 @@ namespace scripting
{
std::vector<std::string> results;
results.push_back(utils::string::va("_id_%X", id));
results.push_back(utils::string::va("_ID%i", id));
for (const auto& token : token_map)
{
if (token.second == id)
{
results.push_back(token.first);
break;
}
}
results.push_back(utils::string::va("_id_%04X", id));
results.push_back(xsk::gsc::h2::resolver::token_name(static_cast<std::uint16_t>(id)));
return results;
}
std::string find_token_single(unsigned int id)
{
std::vector<std::string> results;
for (const auto& token : token_map)
{
if (token.second == id)
{
return token.first;
}
}
return utils::string::va("_id_%X", id);
return xsk::gsc::h2::resolver::token_name(static_cast<std::uint16_t>(id));
}
unsigned int find_token_id(const std::string& name)
{
const auto result = token_map.find(name);
if (result != token_map.end())
const auto id = xsk::gsc::h2::resolver::token_id(name);
if (id != 0)
{
return result->second;
return id;
}
const auto parsed_id = parse_token_id(name);
@ -143,7 +98,10 @@ namespace scripting
script_function find_function(const std::string& name, const bool prefer_global)
{
const auto index = find_function_index(name, prefer_global);
if (index < 0) return nullptr;
if (index < 0)
{
return nullptr;
}
return get_function_by_index(index);
}

View File

@ -3,11 +3,6 @@
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;
extern std::unordered_map<unsigned, std::string> file_list;
using script_function = void(*)(game::scr_entref_t);
std::vector<std::string> find_token(unsigned int id);

View File

@ -16,6 +16,10 @@
#include "game/ui_scripting/execution.hpp"
#include <xsk/gsc/types.hpp>
#include <xsk/resolver.hpp>
#include <xsk/utils/compression.hpp>
#include <utils/string.hpp>
#include <utils/io.hpp>
#include <utils/nt.hpp>
@ -336,9 +340,10 @@ namespace scripting::lua
auto entity_type = state.new_usertype<entity>("entity");
for (const auto& func : method_map)
for (const auto& func : xsk::gsc::h2::resolver::get_methods())
{
const auto name = utils::string::to_lower(func.first);
const auto func_name = std::string(func.first);
const auto name = utils::string::to_lower(func_name);
entity_type[name.data()] = [name](const entity& entity, const sol::this_state s, sol::variadic_args va)
{
std::vector<script_value> arguments{};
@ -469,9 +474,10 @@ namespace scripting::lua
auto game_type = state.new_usertype<game>("game_");
state["game"] = game();
for (const auto& func : function_map)
for (const auto& func : xsk::gsc::h2::resolver::get_functions())
{
const auto name = utils::string::to_lower(func.first);
const auto func_name = std::string(func.first);
const auto name = utils::string::to_lower(func_name);
game_type[name] = [name](const game&, const sol::this_state s, sol::variadic_args va)
{
std::vector<script_value> arguments{};

View File

@ -18,7 +18,7 @@ namespace game
WEAK symbol<void(int localClientNum, const char* text)> Cbuf_AddText{0x14059A050};
WEAK symbol<void(int localClientNum, const char* message)> CG_GameMessage{0x14037F450};
WEAK symbol<void(int localClientNum, const char* message, int style)> CG_GameMessage{0x14037F450};
WEAK symbol<void(int localClientNum, const char* message)> CG_GameMessageBold{0x14037F1B0};
WEAK symbol<char*(const unsigned int weapon,
bool isAlternate, char* outputBuffer, int bufferLen)> CG_GetWeaponDisplayName{0x1403B9210};
@ -48,6 +48,8 @@ namespace game
WEAK symbol<size_t(XAssetType type)> DB_GetXAssetTypeSize{0x1403E40D0};
WEAK symbol<void(void* levelLoad, const char* name,
const unsigned int allocFlags, const unsigned __int64 sizeEst)> DB_LevelLoadAddZone{0x1404145D0};
WEAK symbol<int(game::XAssetType type, const char* name)> DB_IsXAssetDefault{0x1404143C0};
WEAK symbol<int(game::XAssetType type, const char* name)> DB_XAssetExists{0x140417FD0};
WEAK symbol<dvar_t*(const char* name)> Dvar_FindVar{0x140618F90};
WEAK symbol<dvar_t*(int hash)> Dvar_FindMalleableVar{0x140618F00};

View File

@ -7,6 +7,7 @@
#include <utils/string.hpp>
#include <utils/flags.hpp>
#include <utils/io.hpp>
#include <utils/properties.hpp>
DECLSPEC_NORETURN void WINAPI exit_hook(const int code)
{
@ -59,7 +60,7 @@ void apply_aslr_patch(std::string* data)
void get_aslr_patched_binary(std::string* binary, std::string* data)
{
const auto patched_binary = "h2_sp_patched.exe"s;
const auto patched_binary = (utils::properties::get_appdata_path() / "bin/h2_sp64_bnet_ship.exe"s).generic_string();
try
{
@ -127,6 +128,7 @@ FARPROC load_binary(const launcher::mode mode)
void remove_crash_file()
{
utils::io::remove_file("__h2Exe");
utils::io::remove_file("h2_sp_patched.exe"); // remove this at some point
}
void verify_version()

View File

@ -0,0 +1,24 @@
#include "io.hpp"
#include "properties.hpp"
#include <gsl/gsl>
#include <ShlObj.h>
namespace utils::properties
{
std::filesystem::path get_appdata_path()
{
PWSTR path;
if (!SUCCEEDED(SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr, &path)))
{
throw std::runtime_error("Failed to read APPDATA path!");
}
auto _ = gsl::finally([&path]
{
CoTaskMemFree(path);
});
static auto appdata = std::filesystem::path(path) / "h2-mod";
return appdata;
}
}

View File

@ -0,0 +1,6 @@
#pragma once
namespace utils::properties
{
std::filesystem::path get_appdata_path();
}