update gsc
This commit is contained in:
parent
fc049cf695
commit
089fd82be9
@ -16,24 +16,24 @@ namespace gsc
|
|||||||
//sub_1400B2BE0
|
//sub_1400B2BE0
|
||||||
{0x1400b2ced,""},
|
{0x1400b2ced,""},
|
||||||
//sub_1400F4F90
|
//sub_1400F4F90
|
||||||
{0x1400f5142,""},
|
{0x1400f5142,"Invalid match data definition specified."},
|
||||||
{0x1400f5246,""},
|
{0x1400f5246,"Invalid match data definition specified."},
|
||||||
{0x1400f52b2,""},
|
{0x1400f52b2,"Invalid match data definition specified."},
|
||||||
{0x1400f53e2,""},
|
{0x1400f53e2,"Invalid match data definition specified."},
|
||||||
{0x1400f544f,""},
|
{0x1400f544f,"Invalid match data definition specified."},
|
||||||
//sub_1400F5AC0
|
//sub_1400F5AC0
|
||||||
{0x1400f5c4e,""},
|
{0x1400f5c4e,"Invalid match data definition specified."},
|
||||||
{0x1400f5cbb,""},
|
{0x1400f5cbb,"Invalid match data definition specified."},
|
||||||
//sub_1400F5EF0
|
//sub_1400F5EF0
|
||||||
{0x1400f60ca,""},
|
{0x1400f60ca,"Invalid match data definition specified."},
|
||||||
{0x1400f6139,""},
|
{0x1400f6139,"Invalid match data definition specified."},
|
||||||
//sub_1400F6440
|
//sub_1400F6440
|
||||||
{0x1400f64b7,""},
|
{0x1400f64b7,"Invalid match data definition specified. lifeCount must be a short"},
|
||||||
//sub_1400F6540
|
//sub_1400F6540
|
||||||
{0x1400f65e6,""},
|
{0x1400f65e6,"Invalid match data definition specified. The lives field must be an indexed array of life structures"},
|
||||||
{0x1400f660e,""},
|
{0x1400f660e,"Invalid match data definition specified. The players field must be an indexed array of player structures"},
|
||||||
//sub_140117AB0
|
//sub_140117AB0
|
||||||
{0x140117b38,""},
|
{0x140117b38,"ERROR: G_RagdollConstraintEntity_Spawn(), Will not spawn."},
|
||||||
//sub_1403D9B70
|
//sub_1403D9B70
|
||||||
{0x1403d9bac,""},
|
{0x1403d9bac,""},
|
||||||
//sub_1403D9D80
|
//sub_1403D9D80
|
||||||
|
@ -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)
|
void builtin_call_error(const std::string& error)
|
||||||
{
|
{
|
||||||
const auto function_id = get_function_id();
|
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)
|
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);
|
const auto function = find_function(frame->fs.pos);
|
||||||
|
|
||||||
if (function.has_value())
|
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
|
else
|
||||||
{
|
{
|
||||||
console::warn("\tat unknown location %p\n", pos);
|
console::warn("\tat unknown location %p\n", pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
first = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -375,13 +429,20 @@ namespace gsc
|
|||||||
force_error_print = false;
|
force_error_print = false;
|
||||||
gsc_error_msg = {};
|
gsc_error_msg = {};
|
||||||
|
|
||||||
print_callstack();
|
print_callstack(opcode_id);
|
||||||
console::warn("**********************************************\n");
|
console::warn("**********************************************\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void vm_error_stub(__int64 mark_pos)
|
void vm_error_stub(__int64 mark_pos)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
vm_error_internal();
|
vm_error_internal();
|
||||||
|
}
|
||||||
|
catch (xsk::gsc::error& err)
|
||||||
|
{
|
||||||
|
console::error("vm_error: %s\n", err.what());
|
||||||
|
}
|
||||||
|
|
||||||
utils::hook::invoke<void>(0x510C80_b, mark_pos);
|
utils::hook::invoke<void>(0x510C80_b, mark_pos);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
const auto custom = methods.contains(custom_function_id);
|
||||||
if (custom)
|
if (custom)
|
||||||
{
|
{
|
||||||
@ -124,7 +125,7 @@ namespace gsc
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
builtin_method meth = meth_table[function_id - 0x8000];
|
builtin_method meth = meth_table[meth_function_id - 0x8000];
|
||||||
if (meth == nullptr)
|
if (meth == nullptr)
|
||||||
{
|
{
|
||||||
scr_error(utils::string::va("builtin method \"%s\" doesn't exist", gsc_ctx->meth_name(custom_function_id).data()), true);
|
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);
|
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)
|
void vm_call_builtin_method_stub(utils::hook::assembler& a)
|
||||||
{
|
{
|
||||||
//a.pushad64();
|
a.pushad64();
|
||||||
a.push(rdx);
|
a.push(rdx);
|
||||||
a.push(ecx);
|
a.push(ecx);
|
||||||
a.mov(rdx, rdi); // function id is stored in rdi
|
a.mov(rdx, rdi); // function id is stored in rdi
|
||||||
a.mov(ecx, ebx); // ent ref is stored in ebx
|
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(rdx);
|
||||||
a.pop(ecx);
|
a.pop(ecx);
|
||||||
//a.popad64();
|
a.popad64();
|
||||||
|
|
||||||
a.jmp(0xC0E8F9_b);
|
a.jmp(0xC0E8F2_b);
|
||||||
}
|
}
|
||||||
|
|
||||||
void print(const function_args& args)
|
void print(const function_args& args)
|
||||||
@ -283,7 +289,11 @@ namespace gsc
|
|||||||
public:
|
public:
|
||||||
void post_unpack() override
|
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>(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))));
|
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::inject(0xBFD5AF_b + 3, &meth_table);
|
||||||
utils::hook::set<uint32_t>(0xBFD5B6_b + 2, sizeof(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::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);
|
utils::hook::jump(0xC0D0A4_b, utils::hook::assemble(vm_execute_stub), true);
|
||||||
|
|
||||||
|
@ -19,7 +19,9 @@
|
|||||||
|
|
||||||
namespace gsc
|
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
|
namespace
|
||||||
{
|
{
|
||||||
@ -30,7 +32,6 @@ namespace gsc
|
|||||||
std::unordered_map<std::string, std::uint32_t> init_handles;
|
std::unordered_map<std::string, std::uint32_t> init_handles;
|
||||||
|
|
||||||
utils::memory::allocator scriptfile_allocator;
|
utils::memory::allocator scriptfile_allocator;
|
||||||
std::unordered_map<std::string, game::ScriptFile*> loaded_scripts;
|
|
||||||
|
|
||||||
char* script_mem_buf = nullptr;
|
char* script_mem_buf = nullptr;
|
||||||
std::uint64_t script_mem_buf_size = 0;
|
std::uint64_t script_mem_buf_size = 0;
|
||||||
@ -105,6 +106,35 @@ namespace gsc
|
|||||||
return false;
|
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)
|
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())
|
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 assembly_ptr = compiler.compile(real_name, data);
|
||||||
const auto output_script = assembler.assemble(*assembly_ptr);
|
const auto output_script = assembler.assemble(*assembly_ptr);
|
||||||
|
|
||||||
const auto bytecode = output_script.first;
|
const auto bytecode = std::get<0>(output_script);
|
||||||
const auto stack = output_script.second;
|
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)));
|
const auto script_file_ptr = static_cast<game::ScriptFile*>(scriptfile_allocator.allocate(sizeof(game::ScriptFile)));
|
||||||
script_file_ptr->name = file_name;
|
script_file_ptr->name = file_name;
|
||||||
@ -293,21 +326,20 @@ namespace gsc
|
|||||||
xsk::gsc::build::dev :
|
xsk::gsc::build::dev :
|
||||||
xsk::gsc::build::prod;
|
xsk::gsc::build::prod;
|
||||||
|
|
||||||
gsc_ctx->init(comp_mode, [](const std::string& include_name)
|
gsc_ctx->init(comp_mode, []([[maybe_unused]] auto const* ctx, const auto& included_path) -> std::pair<xsk::gsc::buffer, std::vector<std::uint8_t>>
|
||||||
-> 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;
|
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()))
|
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;
|
std::vector<std::uint8_t> script_data;
|
||||||
|
@ -4,6 +4,15 @@
|
|||||||
namespace gsc
|
namespace gsc
|
||||||
{
|
{
|
||||||
extern std::unique_ptr<xsk::gsc::iw7::context> gsc_ctx;
|
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);
|
game::ScriptFile* find_script(game::XAssetType type, const char* name, int allow_create_default);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user