Christmas Day Update
This commit is contained in:
@@ -113,7 +113,6 @@ namespace gsc
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
unsigned int scr_get_object(unsigned int index)
|
||||
@@ -284,28 +283,23 @@ namespace gsc
|
||||
public:
|
||||
void post_unpack() override
|
||||
{
|
||||
if (game::environment::is_sp())
|
||||
{
|
||||
return;
|
||||
}
|
||||
scr_emit_function_hook.create(SELECT_VALUE(0x1403D3350, 0x14042E150), &scr_emit_function_stub);
|
||||
|
||||
scr_emit_function_hook.create(0x14042E150, &scr_emit_function_stub);
|
||||
|
||||
utils::hook::call(0x14042E0E4, compile_error_stub); // LinkFile
|
||||
utils::hook::call(0x14042E138, compile_error_stub); // LinkFile
|
||||
utils::hook::call(0x14042E22B, find_variable_stub); // Scr_EmitFunction
|
||||
utils::hook::call(SELECT_VALUE(0x1403D32E4, 0x14042E0E4), compile_error_stub); // LinkFile
|
||||
utils::hook::call(SELECT_VALUE(0x1403D3338, 0x14042E138), compile_error_stub); // LinkFile
|
||||
utils::hook::call(SELECT_VALUE(0x1403D342A, 0x14042E22B), find_variable_stub); // Scr_EmitFunction
|
||||
|
||||
// Restore basic error messages to scr functions
|
||||
utils::hook::jump(0x140438ED0, scr_get_object);
|
||||
utils::hook::jump(0x140438AD0, scr_get_const_string);
|
||||
utils::hook::jump(0x1404388B0, scr_get_const_istring);
|
||||
utils::hook::jump(0x1404393D0, scr_get_vector);
|
||||
utils::hook::jump(0x140438E10, scr_get_int);
|
||||
utils::hook::jump(0x140438D60, scr_get_float);
|
||||
utils::hook::jump(game::Scr_GetObject, scr_get_object);
|
||||
utils::hook::jump(game::Scr_GetConstString, scr_get_const_string);
|
||||
utils::hook::jump(game::Scr_GetConstIString, scr_get_const_istring);
|
||||
utils::hook::jump(game::Scr_GetVector, scr_get_vector);
|
||||
utils::hook::jump(game::Scr_GetInt, scr_get_int);
|
||||
utils::hook::jump(game::Scr_GetFloat, scr_get_float);
|
||||
|
||||
utils::hook::jump(0x1404390B0, scr_get_pointer_type);
|
||||
utils::hook::jump(0x140439280, scr_get_type);
|
||||
utils::hook::jump(0x1404392F0, scr_get_type_name);
|
||||
utils::hook::jump(SELECT_VALUE(0x1403DE150, 0x1404390B0), scr_get_pointer_type);
|
||||
utils::hook::jump(SELECT_VALUE(0x1403DE320, 0x140439280), scr_get_type);
|
||||
utils::hook::jump(SELECT_VALUE(0x1403DE390, 0x1404392F0), scr_get_type_name);
|
||||
}
|
||||
|
||||
void pre_destroy() override
|
||||
|
||||
@@ -37,9 +37,60 @@ namespace gsc
|
||||
bool force_error_print = false;
|
||||
std::optional<std::string> gsc_error_msg;
|
||||
|
||||
std::vector<devmap_entry> devmap_entries{};
|
||||
|
||||
std::optional<devmap_entry> get_devmap_entry(const std::uint8_t* codepos)
|
||||
{
|
||||
const auto itr = std::ranges::find_if(devmap_entries, [codepos](const devmap_entry& entry) -> bool
|
||||
{
|
||||
return codepos >= entry.bytecode && codepos < entry.bytecode + entry.size;
|
||||
});
|
||||
|
||||
if (itr != devmap_entries.end())
|
||||
{
|
||||
return *itr;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<std::pair<std::uint16_t, std::uint16_t>> get_line_and_col_for_codepos(const std::uint8_t* codepos)
|
||||
{
|
||||
const auto entry = get_devmap_entry(codepos);
|
||||
|
||||
if (!entry.has_value())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<std::pair<std::uint16_t, std::uint16_t>> best_line_info{};
|
||||
std::uint32_t best_codepos = 0;
|
||||
|
||||
assert(codepos >= entry->bytecode);
|
||||
const std::uint32_t codepos_offset = static_cast<std::uint32_t>(codepos - entry->bytecode);
|
||||
|
||||
for (const auto& instruction : entry->devmap)
|
||||
{
|
||||
if (instruction.codepos > codepos_offset)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (best_line_info.has_value() && codepos_offset - instruction.codepos > codepos_offset - best_codepos)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
best_line_info = { { instruction.line, instruction.col } };
|
||||
best_codepos = instruction.codepos;
|
||||
}
|
||||
|
||||
return best_line_info;
|
||||
}
|
||||
|
||||
unsigned int scr_get_function_stub(const char** p_name, int* type)
|
||||
{
|
||||
const auto result = utils::hook::invoke<unsigned int>(0x1403CD9F0, p_name, type);
|
||||
const auto result = game::Scr_GetFunction(p_name, type);
|
||||
|
||||
for (const auto& [name, func] : functions)
|
||||
{
|
||||
@@ -120,27 +171,36 @@ namespace gsc
|
||||
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);
|
||||
|
||||
const char* location;
|
||||
if (function.has_value())
|
||||
{
|
||||
console::warn("\tat function \"%s\" in file \"%s.gsc\"\n", function.value().first.data(), function.value().second.data());
|
||||
location = utils::string::va("function \"%s\" in file \"%s\"", function.value().first.data(), function.value().second.data());
|
||||
}
|
||||
else
|
||||
{
|
||||
console::warn("\tat unknown location %p\n", pos);
|
||||
location = utils::string::va("unknown location %p", pos);
|
||||
}
|
||||
|
||||
const auto line_info = get_line_and_col_for_codepos(reinterpret_cast<const std::uint8_t*>(pos));
|
||||
if (line_info.has_value())
|
||||
{
|
||||
location = utils::string::va("%s line \"%d\" column \"%d\"", location, line_info->first, line_info->second);
|
||||
}
|
||||
|
||||
console::warn("\tat %s\n", location);
|
||||
}
|
||||
}
|
||||
|
||||
void vm_error_stub(int mark_pos)
|
||||
void vm_error_stub(const unsigned __int64 mark_pos)
|
||||
{
|
||||
if (!dvars::com_developer_script->current.enabled && !force_error_print)
|
||||
{
|
||||
utils::hook::invoke<void>(0x1404E4D00, mark_pos);
|
||||
game::LargeLocalResetToMark(mark_pos);
|
||||
return;
|
||||
}
|
||||
|
||||
console::warn("******* script runtime error ********\n");
|
||||
const auto opcode_id = *reinterpret_cast<std::uint8_t*>(0x144D57840);
|
||||
const auto opcode_id = *reinterpret_cast<std::uint8_t*>(SELECT_VALUE(0x1455BE740, 0x144D57840));
|
||||
|
||||
const std::string error = gsc_error_msg.has_value() ? std::format(": {}", gsc_error_msg.value()) : std::string();
|
||||
|
||||
@@ -166,7 +226,7 @@ namespace gsc
|
||||
|
||||
print_callstack();
|
||||
console::warn("************************************\n");
|
||||
utils::hook::invoke<void>(0x1404E4D00, mark_pos);
|
||||
game::LargeLocalResetToMark(mark_pos);
|
||||
}
|
||||
|
||||
void inc_in_param()
|
||||
@@ -228,6 +288,11 @@ namespace gsc
|
||||
scr_error(utils::string::va("Assert fail: %s", game::Scr_GetString(0)));
|
||||
}
|
||||
|
||||
void scr_cmd_is_dedicated_server()
|
||||
{
|
||||
game::Scr_AddInt(game::environment::is_dedi());
|
||||
}
|
||||
|
||||
const char* get_code_pos(const int index)
|
||||
{
|
||||
if (static_cast<unsigned int>(index) >= game::scr_VmPub->outparamcount)
|
||||
@@ -248,6 +313,22 @@ namespace gsc
|
||||
}
|
||||
}
|
||||
|
||||
void add_devmap_entry(std::uint8_t* codepos, std::size_t size, const std::string& name, xsk::gsc::buffer devmap_buf)
|
||||
{
|
||||
std::vector<dev_map_instruction> devmap{};
|
||||
const auto* devmap_ptr = reinterpret_cast<const dev_map*>(devmap_buf.data);
|
||||
|
||||
devmap.resize(devmap_ptr->num_instructions);
|
||||
std::memcpy(devmap.data(), devmap_ptr->instructions, sizeof(dev_map_instruction) * devmap_ptr->num_instructions);
|
||||
|
||||
devmap_entries.emplace_back(codepos, size, name, std::move(devmap));
|
||||
}
|
||||
|
||||
void clear_devmap()
|
||||
{
|
||||
devmap_entries.clear();
|
||||
}
|
||||
|
||||
void add_function(const std::string& name, game::BuiltinFunction function)
|
||||
{
|
||||
++function_id_start;
|
||||
@@ -271,27 +352,18 @@ namespace gsc
|
||||
utils::hook::set<game::BuiltinFunction>(SELECT_VALUE(0x14086F468, 0x1409E6CE8), scr_print);
|
||||
utils::hook::set<game::BuiltinFunction>(SELECT_VALUE(0x14086F480, 0x1409E6D00), scr_print_ln);
|
||||
|
||||
utils::hook::set<std::uint32_t>(SELECT_VALUE(0x1403D353C, 0x14042E33C), 0x1000); // Scr_RegisterFunction
|
||||
utils::hook::set<std::uint32_t>(SELECT_VALUE(0x1403D353B + 1, 0x14042E33B + 1), 0x1000); // Scr_RegisterFunction
|
||||
|
||||
utils::hook::set<std::uint32_t>(SELECT_VALUE(0x1403D3542 + 4, 0x14042E342 + 4), RVA(&func_table)); // Scr_RegisterFunction
|
||||
utils::hook::set<std::uint32_t>(SELECT_VALUE(0x1403E0BDD + 3, 0x14043BBBE + 3), RVA(&func_table)); // VM_Execute_0
|
||||
// utils::hook::set<std::uint32_t>(SELECT_VALUE(0x1403E0BDD + 3, 0x14043BBBE + 3), RVA(&func_table)); // VM_Execute_0
|
||||
utils::hook::inject(SELECT_VALUE(0x1403D38E4 + 3, 0x14042E734 + 3), &func_table); // Scr_BeginLoadScripts
|
||||
|
||||
if (game::environment::is_sp())
|
||||
{
|
||||
return;
|
||||
}
|
||||
utils::hook::nop(SELECT_VALUE(0x1403E0BDD + 5, 0x14043BBBE + 5), 2);
|
||||
utils::hook::call(SELECT_VALUE(0x1403E0BDD, 0x14043BBBE), vm_call_builtin_function);
|
||||
|
||||
utils::hook::nop(0x14043BBBE + 5, 2);
|
||||
utils::hook::call(0x14043BBBE, vm_call_builtin_function);
|
||||
utils::hook::call(SELECT_VALUE(0x1403D391F, 0x14042E76F), scr_get_function_stub);
|
||||
|
||||
utils::hook::call(0x14043CEB1, vm_error_stub);
|
||||
|
||||
utils::hook::call(0x14042E76F, scr_get_function_stub);
|
||||
|
||||
utils::hook::set<game::BuiltinFunction>(0x1409E6E38, assert_ex_cmd);
|
||||
utils::hook::set<game::BuiltinFunction>(0x1409E6E50, assert_msg_cmd);
|
||||
utils::hook::set<game::BuiltinFunction>(0x1409E6E20, assert_cmd);
|
||||
utils::hook::call(SELECT_VALUE(0x1403E1ED0, 0x14043CEB1), vm_error_stub);
|
||||
|
||||
add_function("replacefunc", []
|
||||
{
|
||||
@@ -309,10 +381,16 @@ namespace gsc
|
||||
command::execute(cmd);
|
||||
});
|
||||
|
||||
add_function("isdedicated", []
|
||||
if (game::environment::is_sp())
|
||||
{
|
||||
game::Scr_AddInt(game::environment::is_dedi());
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
utils::hook::set<game::BuiltinFunction>(0x1409E6E38, assert_ex_cmd);
|
||||
utils::hook::set<game::BuiltinFunction>(0x1409E6E50, assert_msg_cmd);
|
||||
utils::hook::set<game::BuiltinFunction>(0x1409E6E20, assert_cmd);
|
||||
|
||||
utils::hook::set<game::BuiltinFunction>(0x1409E94D0, scr_cmd_is_dedicated_server);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,9 +1,36 @@
|
||||
#pragma once
|
||||
#include <xsk/gsc/engine/iw6_pc.hpp>
|
||||
|
||||
namespace gsc
|
||||
{
|
||||
extern void* func_table[0x1000];
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct dev_map_instruction
|
||||
{
|
||||
std::uint32_t codepos;
|
||||
std::uint16_t line;
|
||||
std::uint16_t col;
|
||||
};
|
||||
|
||||
struct dev_map
|
||||
{
|
||||
std::uint32_t num_instructions;
|
||||
dev_map_instruction instructions[1];
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
struct devmap_entry
|
||||
{
|
||||
const std::uint8_t* bytecode;
|
||||
std::size_t size;
|
||||
std::string script_name;
|
||||
std::vector<dev_map_instruction> devmap;
|
||||
};
|
||||
|
||||
void add_devmap_entry(std::uint8_t*, std::size_t, const std::string&, xsk::gsc::buffer);
|
||||
void clear_devmap();
|
||||
|
||||
void add_function(const std::string& name, game::BuiltinFunction function);
|
||||
|
||||
void scr_error(const char* error);
|
||||
|
||||
@@ -3,15 +3,16 @@
|
||||
#include "game/game.hpp"
|
||||
#include "game/dvars.hpp"
|
||||
|
||||
#include <utils/io.hpp>
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/memory.hpp>
|
||||
#include <utils/compression.hpp>
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/io.hpp>
|
||||
#include <utils/memory.hpp>
|
||||
|
||||
#include "component/filesystem.hpp"
|
||||
#include "component/console.hpp"
|
||||
#include "component/filesystem.hpp"
|
||||
#include "component/scripting.hpp"
|
||||
|
||||
#include "script_extension.hpp"
|
||||
#include "script_loading.hpp"
|
||||
|
||||
namespace gsc
|
||||
@@ -32,6 +33,7 @@ namespace gsc
|
||||
init_handles.clear();
|
||||
loaded_scripts.clear();
|
||||
script_allocator.clear();
|
||||
clear_devmap();
|
||||
}
|
||||
|
||||
bool read_raw_script_file(const std::string& name, std::string* data)
|
||||
@@ -114,6 +116,12 @@ namespace gsc
|
||||
|
||||
loaded_scripts[real_name] = script_file_ptr;
|
||||
|
||||
const auto devmap = std::get<2>(output_script);
|
||||
if (devmap.size > 0 && (gsc_ctx->build() & xsk::gsc::build::dev_maps) != xsk::gsc::build::prod)
|
||||
{
|
||||
add_devmap_entry(script_file_ptr->bytecode, byte_code_size, real_name, devmap);
|
||||
}
|
||||
|
||||
return script_file_ptr;
|
||||
}
|
||||
catch (const std::exception& ex)
|
||||
@@ -180,13 +188,9 @@ namespace gsc
|
||||
}
|
||||
}
|
||||
|
||||
void load_scripts(const std::filesystem::path& root_dir)
|
||||
void load_scripts_from_folder(const std::filesystem::path& root_dir, const std::filesystem::path& script_dir)
|
||||
{
|
||||
const std::filesystem::path script_dir = root_dir / "scripts";
|
||||
if (!utils::io::directory_exists(script_dir.generic_string()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
console::info("Scanning directory '%s' for custom GSC scripts...\n", script_dir.generic_string().data());
|
||||
|
||||
const auto scripts = utils::io::list_files(script_dir.generic_string());
|
||||
for (const auto& script : scripts)
|
||||
@@ -204,6 +208,44 @@ namespace gsc
|
||||
}
|
||||
}
|
||||
|
||||
void load_scripts(const std::filesystem::path& root_dir)
|
||||
{
|
||||
const auto load = [&root_dir](const std::filesystem::path& folder) -> void
|
||||
{
|
||||
const std::filesystem::path script_dir = root_dir / folder;
|
||||
if (utils::io::directory_exists(script_dir.generic_string()))
|
||||
{
|
||||
load_scripts_from_folder(root_dir, script_dir);
|
||||
}
|
||||
};
|
||||
|
||||
const std::filesystem::path base_dir = "scripts";
|
||||
|
||||
load(base_dir);
|
||||
|
||||
const auto* map_name = game::Dvar_FindVar("mapname");
|
||||
|
||||
if (game::environment::is_sp())
|
||||
{
|
||||
const std::filesystem::path game_folder = "sp";
|
||||
|
||||
load(base_dir / game_folder);
|
||||
|
||||
load(base_dir / game_folder / map_name->current.string);
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::filesystem::path game_folder = "mp";
|
||||
|
||||
load(base_dir / game_folder);
|
||||
|
||||
load(base_dir / game_folder / map_name->current.string);
|
||||
|
||||
const auto* game_type = game::Dvar_FindVar("g_gametype");
|
||||
load(base_dir / game_folder / game_type->current.string);
|
||||
}
|
||||
}
|
||||
|
||||
int db_is_x_asset_default(game::XAssetType type, const char* name)
|
||||
{
|
||||
if (loaded_scripts.contains(name))
|
||||
@@ -218,12 +260,22 @@ namespace gsc
|
||||
{
|
||||
utils::hook::invoke<void>(0x1403CCB10);
|
||||
|
||||
clear();
|
||||
for (const auto& path : filesystem::get_search_paths())
|
||||
{
|
||||
load_scripts(path);
|
||||
}
|
||||
}
|
||||
|
||||
int g_scr_load_script_and_label_stub(game::ScriptFunctions* functions)
|
||||
{
|
||||
const auto result = utils::hook::invoke<int>(0x140349790, functions);
|
||||
|
||||
for (const auto& path : filesystem::get_search_paths())
|
||||
{
|
||||
load_scripts(path);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void db_get_raw_buffer_stub(const game::RawFile* rawfile, char* buf, const int size)
|
||||
@@ -250,7 +302,26 @@ namespace gsc
|
||||
utils::hook::invoke<void>(0x1403D2CA0);
|
||||
}
|
||||
|
||||
void scr_load_level_stub()
|
||||
void scr_load_level_singleplayer_stub()
|
||||
{
|
||||
for (auto& function_handle : main_handles)
|
||||
{
|
||||
console::info("Executing '%s::main'\n", function_handle.first.data());
|
||||
const auto thread = game::Scr_ExecThread(static_cast<int>(function_handle.second), 0);
|
||||
game::RemoveRefToObject(thread);
|
||||
}
|
||||
|
||||
utils::hook::invoke<void>(0x1403401B0);
|
||||
|
||||
for (auto& function_handle : init_handles)
|
||||
{
|
||||
console::info("Executing '%s::init'\n", function_handle.first.data());
|
||||
const auto thread = game::Scr_ExecThread(static_cast<int>(function_handle.second), 0);
|
||||
game::RemoveRefToObject(thread);
|
||||
}
|
||||
}
|
||||
|
||||
void scr_load_level_multiplayer_stub()
|
||||
{
|
||||
utils::hook::invoke<void>(0x1403CDC60);
|
||||
|
||||
@@ -345,31 +416,35 @@ namespace gsc
|
||||
utils::hook::call(SELECT_VALUE(0x14032D1E0, 0x1403CCED9), scr_begin_load_scripts_stub); // GScr_LoadScripts
|
||||
utils::hook::call(SELECT_VALUE(0x14032D345, 0x1403CD08D), scr_end_load_scripts_stub); // GScr_LoadScripts
|
||||
|
||||
// ProcessScript
|
||||
utils::hook::call(SELECT_VALUE(0x1403DC887, 0x1404378D7), find_script);
|
||||
utils::hook::call(SELECT_VALUE(0x1403DC897, 0x1404378E7), db_is_x_asset_default);
|
||||
|
||||
dvars::com_developer_script = game::Dvar_RegisterBool("developer_script", false, game::DVAR_FLAG_NONE, "Enable developer script comments");
|
||||
|
||||
if (game::environment::is_sp())
|
||||
scripting::on_shutdown([](const int clear_scripts, const int post_shutdown) -> void
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// ProcessScript
|
||||
utils::hook::call(0x1404378D7, find_script);
|
||||
utils::hook::call(0x1404378E7, db_is_x_asset_default);
|
||||
|
||||
// GScr_LoadScripts
|
||||
utils::hook::call(0x1403CD009, gscr_load_game_type_script_stub);
|
||||
|
||||
// Exec script handles
|
||||
utils::hook::call(0x14039F64E, g_load_structs_stub);
|
||||
utils::hook::call(0x14039F653, scr_load_level_stub);
|
||||
|
||||
scripting::on_shutdown([](int free_scripts)
|
||||
{
|
||||
if (free_scripts)
|
||||
if (clear_scripts && post_shutdown)
|
||||
{
|
||||
clear();
|
||||
}
|
||||
});
|
||||
|
||||
if (game::environment::is_sp())
|
||||
{
|
||||
utils::hook::call(0x14034996F, g_scr_load_script_and_label_stub);
|
||||
|
||||
utils::hook::call(0x140316591, scr_load_level_singleplayer_stub);
|
||||
}
|
||||
else
|
||||
{
|
||||
// GScr_LoadScripts
|
||||
utils::hook::call(0x1403CD009, gscr_load_game_type_script_stub);
|
||||
|
||||
// Exec script handles
|
||||
utils::hook::call(0x14039F64E, g_load_structs_stub);
|
||||
utils::hook::call(0x14039F653, scr_load_level_multiplayer_stub);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user