update gsc

This commit is contained in:
quaK 2024-06-29 00:19:02 +03:00
parent fc049cf695
commit 089fd82be9
5 changed files with 151 additions and 38 deletions

View File

@ -16,24 +16,24 @@ namespace gsc
//sub_1400B2BE0
{0x1400b2ced,""},
//sub_1400F4F90
{0x1400f5142,""},
{0x1400f5246,""},
{0x1400f52b2,""},
{0x1400f53e2,""},
{0x1400f544f,""},
{0x1400f5142,"Invalid match data definition specified."},
{0x1400f5246,"Invalid match data definition specified."},
{0x1400f52b2,"Invalid match data definition specified."},
{0x1400f53e2,"Invalid match data definition specified."},
{0x1400f544f,"Invalid match data definition specified."},
//sub_1400F5AC0
{0x1400f5c4e,""},
{0x1400f5cbb,""},
{0x1400f5c4e,"Invalid match data definition specified."},
{0x1400f5cbb,"Invalid match data definition specified."},
//sub_1400F5EF0
{0x1400f60ca,""},
{0x1400f6139,""},
{0x1400f60ca,"Invalid match data definition specified."},
{0x1400f6139,"Invalid match data definition specified."},
//sub_1400F6440
{0x1400f64b7,""},
{0x1400f64b7,"Invalid match data definition specified. lifeCount must be a short"},
//sub_1400F6540
{0x1400f65e6,""},
{0x1400f660e,""},
{0x1400f65e6,"Invalid match data definition specified. The lives field must be an indexed array of life structures"},
{0x1400f660e,"Invalid match data definition specified. The players field must be an indexed array of player structures"},
//sub_140117AB0
{0x140117b38,""},
{0x140117b38,"ERROR: G_RagdollConstraintEntity_Spawn(), Will not spawn."},
//sub_1403D9B70
{0x1403d9bac,""},
//sub_1403D9D80

View File

@ -306,6 +306,19 @@ namespace gsc
}
}
std::uint32_t get_opcode_size(const std::uint8_t opcode)
{
try
{
const auto index = gsc_ctx->opcode_enum(opcode);
return gsc_ctx->opcode_size(index);
}
catch (...)
{
return 0;
}
}
void builtin_call_error(const std::string& error)
{
const auto function_id = get_function_id();
@ -320,21 +333,62 @@ namespace gsc
}
}
void print_callstack()
void print_callstack(uint8_t opcode_id)
{
bool first = true;
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_stack = game::scr_function_stack;
const auto pos = frame == game::scr_VmPub->function_frame ? 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\"\n", function.value().first.data(), function.value().second.data());
auto& file_name = function.value().second;
if (gsc::source_pos_map.contains(file_name))
{
const auto script = gsc::loaded_scripts[file_name];
assert(script);
const auto& pos_map = gsc::source_pos_map[file_name];
std::uint32_t opcode_size = 0;
if (first)
{
opcode_size = get_opcode_size(opcode_id);
}
if (!opcode_size)
{
opcode_size = 3; // function call
}
auto position = static_cast<std::uint32_t>(pos - script->bytecode);
position = position + opcode_size;
if (pos_map.contains(position))
{
const auto& info = pos_map.at(position);
console::warn("\tat function \"%s\" in file \"%s.gsc\" (line %d, col %d)\n",
function.value().first.data(), file_name.data(), info.line, info.column);
}
else
{
goto NO_DEVMAP;
}
}
else
{
NO_DEVMAP:
console::warn("\tat function \"%s\" in file \"%s.gsc\"\n", function.value().first.data(), file_name.data());
}
}
else
{
console::warn("\tat unknown location %p\n", pos);
}
first = false;
}
}
@ -375,13 +429,20 @@ namespace gsc
force_error_print = false;
gsc_error_msg = {};
print_callstack();
print_callstack(opcode_id);
console::warn("**********************************************\n");
}
void vm_error_stub(__int64 mark_pos)
{
try
{
vm_error_internal();
}
catch (xsk::gsc::error& err)
{
console::error("vm_error: %s\n", err.what());
}
utils::hook::invoke<void>(0x510C80_b, mark_pos);
}

View File

@ -114,9 +114,10 @@ namespace gsc
}
}
void vm_call_builtin_method_internal(game::scr_entref_t ent_ref, int function_id)
int meth_function_id = 0;
void vm_call_builtin_method_internal(game::scr_entref_t ent_ref)
{
const auto custom_function_id = static_cast<std::uint16_t>(function_id); // cast for gsc-tool & our custom method map
const auto custom_function_id = static_cast<std::uint16_t>(meth_function_id); // cast for gsc-tool & our custom method map
const auto custom = methods.contains(custom_function_id);
if (custom)
{
@ -124,7 +125,7 @@ namespace gsc
return;
}
builtin_method meth = meth_table[function_id - 0x8000];
builtin_method meth = meth_table[meth_function_id - 0x8000];
if (meth == nullptr)
{
scr_error(utils::string::va("builtin method \"%s\" doesn't exist", gsc_ctx->meth_name(custom_function_id).data()), true);
@ -134,19 +135,24 @@ namespace gsc
meth(ent_ref);
}
void save_meth_func_id([[maybe_unused]] game::scr_entref_t ent_ref, int function_id)
{
meth_function_id = function_id;
}
void vm_call_builtin_method_stub(utils::hook::assembler& a)
{
//a.pushad64();
a.pushad64();
a.push(rdx);
a.push(ecx);
a.mov(rdx, rdi); // function id is stored in rdi
a.mov(ecx, ebx); // ent ref is stored in ebx
a.call(vm_call_builtin_method_internal);
a.call_aligned(save_meth_func_id);
a.pop(rdx);
a.pop(ecx);
//a.popad64();
a.popad64();
a.jmp(0xC0E8F9_b);
a.jmp(0xC0E8F2_b);
}
void print(const function_args& args)
@ -283,7 +289,11 @@ namespace gsc
public:
void post_unpack() override
{
developer_script = game::Dvar_RegisterBool("developer_script", true, 0, "Enable developer script comments"); // enable by default for now
#ifdef DEBUG
developer_script = game::Dvar_RegisterBool("developer_script", true, 0, "Enable developer script comments");
#else
developer_script = game::Dvar_RegisterBool("developer_script", false, 0, "Enable developer script comments");
#endif
utils::hook::set<uint32_t>(0xBFD16B_b + 1, func_table_count); // change builtin func count
utils::hook::set<uint32_t>(0xBFD172_b + 4, static_cast<uint32_t>(reverse_b((&func_table))));
@ -296,7 +306,8 @@ namespace gsc
utils::hook::inject(0xBFD5AF_b + 3, &meth_table);
utils::hook::set<uint32_t>(0xBFD5B6_b + 2, sizeof(meth_table));
utils::hook::nop(0xC0E8EB_b, 14); // nop the lea & call at the end of call_builtin_method
utils::hook::jump(0xC0E8EB_b, utils::hook::assemble(vm_call_builtin_method_stub), true);
utils::hook::jump(0xC0E8EB_b, utils::hook::assemble(vm_call_builtin_method_stub));
utils::hook::call(0xC0E8F2_b, vm_call_builtin_method_internal);
utils::hook::jump(0xC0D0A4_b, utils::hook::assemble(vm_execute_stub), true);

View File

@ -19,7 +19,9 @@
namespace gsc
{
std::unique_ptr<xsk::gsc::iw7::context> gsc_ctx = std::make_unique<xsk::gsc::iw7::context>();;
std::unique_ptr<xsk::gsc::iw7::context> gsc_ctx = std::make_unique<xsk::gsc::iw7::context>();
std::unordered_map<std::string, game::ScriptFile*> loaded_scripts;
std::unordered_map<std::string, code_pos_map> source_pos_map;
namespace
{
@ -30,7 +32,6 @@ namespace gsc
std::unordered_map<std::string, std::uint32_t> init_handles;
utils::memory::allocator scriptfile_allocator;
std::unordered_map<std::string, game::ScriptFile*> loaded_scripts;
char* script_mem_buf = nullptr;
std::uint64_t script_mem_buf_size = 0;
@ -105,6 +106,35 @@ namespace gsc
return false;
}
void parse_gsc_map(const std::string& name, const xsk::gsc::buffer& devmap)
{
auto data = devmap.data;
auto offset = 0;
auto count = *reinterpret_cast<const int*>(data + offset);
offset += sizeof(uint32_t);
console::debug("parsing gscmap for \"%s\" (%d)\n", name.data(), count);
code_pos_map pos_map{};
for (auto i = 0; i < count; ++i)
{
auto pos = *reinterpret_cast<const uint32_t*>(data + offset);
offset += sizeof(uint32_t);
auto line = *reinterpret_cast<const uint16_t*>(data + offset);
offset += sizeof(uint16_t);
auto column = *reinterpret_cast<const uint16_t*>(data + offset);
offset += sizeof(uint16_t);
pos_map[pos] = { line, column };
}
source_pos_map[name] = pos_map;
}
game::ScriptFile* load_custom_script(const char* file_name, const std::string& real_name)
{
if (const auto itr = loaded_scripts.find(file_name); itr != loaded_scripts.end())
@ -149,8 +179,11 @@ namespace gsc
const auto assembly_ptr = compiler.compile(real_name, data);
const auto output_script = assembler.assemble(*assembly_ptr);
const auto bytecode = output_script.first;
const auto stack = output_script.second;
const auto bytecode = std::get<0>(output_script);
const auto stack = std::get<1>(output_script);
const auto devmap = std::get<2>(output_script);
parse_gsc_map(real_name, devmap);
const auto script_file_ptr = static_cast<game::ScriptFile*>(scriptfile_allocator.allocate(sizeof(game::ScriptFile)));
script_file_ptr->name = file_name;
@ -293,21 +326,20 @@ namespace gsc
xsk::gsc::build::dev :
xsk::gsc::build::prod;
gsc_ctx->init(comp_mode, [](const std::string& include_name)
-> std::pair<xsk::gsc::buffer, std::vector<std::uint8_t>>
gsc_ctx->init(comp_mode, []([[maybe_unused]] auto const* ctx, const auto& included_path) -> std::pair<xsk::gsc::buffer, std::vector<std::uint8_t>>
{
const auto real_name = get_raw_script_file_name(include_name);
const auto script_name = std::filesystem::path(included_path).replace_extension().string();
std::string file_buffer;
if (!read_raw_script_file(real_name, &file_buffer) || file_buffer.empty())
if (!read_raw_script_file(script_name, &file_buffer) || file_buffer.empty())
{
const auto name = get_script_file_name(include_name);
const auto name = get_script_file_name(script_name);
if (game::DB_XAssetExists(game::ASSET_TYPE_SCRIPTFILE, name.data()))
{
return read_compiled_script_file(name, real_name);
return read_compiled_script_file(name, script_name);
}
throw std::runtime_error(std::format("Could not load gsc file '{}'", real_name));
throw std::runtime_error(std::format("Could not load gsc file '{}'", script_name));
}
std::vector<std::uint8_t> script_data;

View File

@ -4,6 +4,15 @@
namespace gsc
{
extern std::unique_ptr<xsk::gsc::iw7::context> gsc_ctx;
extern std::unordered_map<std::string, game::ScriptFile*> loaded_scripts;
struct source_pos_info
{
xsk::u16 line;
xsk::u16 column;
};
using code_pos_map = std::map<uint32_t, source_pos_info>;
extern std::unordered_map<std::string, code_pos_map> source_pos_map;
game::ScriptFile* find_script(game::XAssetType type, const char* name, int allow_create_default);
}