diff --git a/deps/gsc-tool b/deps/gsc-tool index 4806a285..2dbff1a4 160000 --- a/deps/gsc-tool +++ b/deps/gsc-tool @@ -1 +1 @@ -Subproject commit 4806a285b6e6719f5ea968a4199a34ee86eb7cfb +Subproject commit 2dbff1a408096e418c5b3eda01bc9a91eee6587b diff --git a/src/client/component/gsc/script_error.cpp b/src/client/component/gsc/script_error.cpp index edf782e0..c31fe2cf 100644 --- a/src/client/component/gsc/script_error.cpp +++ b/src/client/component/gsc/script_error.cpp @@ -76,7 +76,7 @@ namespace gsc { const auto& pos = function.value(); unknown_function_error = std::format( - "while processing function '{}' in script '{}':\nunknown script '{}'", pos.first, pos.second, scripting::current_file + "while processing function '{}' in script '{}':\nunknown script '{}'", pos.function, pos.file, scripting::current_file ); } else @@ -293,16 +293,21 @@ namespace gsc } } - std::optional> find_function(const char* pos) + std::optional find_function(const char* pos) { for (const auto& file : scripting::script_function_table_sort) { + const auto first_function = file.second.begin(); for (auto i = file.second.begin(); i != file.second.end() && std::next(i) != file.second.end(); ++i) { const auto next = std::next(i); if (pos >= i->second && pos < next->second) { - return {std::make_pair(i->first, file.first)}; + script_info_t info{}; + info.function = i->first; + info.file = file.first; + info.script_start = first_function->second; + return {info}; } } } diff --git a/src/client/component/gsc/script_error.hpp b/src/client/component/gsc/script_error.hpp index 54ad80b9..de095a63 100644 --- a/src/client/component/gsc/script_error.hpp +++ b/src/client/component/gsc/script_error.hpp @@ -4,5 +4,12 @@ namespace gsc { extern std::array var_typename; - std::optional> find_function(const char* pos); + struct script_info_t + { + const char* script_start; + std::string file; + std::string function; + }; + + std::optional find_function(const char* pos); } diff --git a/src/client/component/gsc/script_extension.cpp b/src/client/component/gsc/script_extension.cpp index 4f1b7ebb..ec6858a2 100644 --- a/src/client/component/gsc/script_extension.cpp +++ b/src/client/component/gsc/script_extension.cpp @@ -130,16 +130,39 @@ 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); + const auto pos = frame == game::scr_VmPub->function_frame ? game::scr_function_stack->pos - 1 : frame->fs.pos; + const auto info = find_function(pos); - if (function.has_value()) + if (!info.has_value()) { - console::warn("\tat function \"%s\" in file \"%s.gsc\"\n", function.value().first.data(), function.value().second.data()); + console::warn("\tat unknown location %p\n", pos); + continue; + } + + const auto& function = info->function; + const auto& file = info->file; + const auto devmap_opt = get_script_devmap(file); + + if (devmap_opt.has_value()) + { + const auto& devmap = devmap_opt.value(); + const auto rel_pos = static_cast(pos - info->script_start); + const auto& iter = devmap->find(rel_pos); + + if (iter != devmap->end()) + { + console::warn("\tat function \"%s\" in file \"%s.gsc\" (line %d, column %d)\n", + function.data(), file.data(), + iter->second.line, iter->second.column); + } + else + { + console::warn("\tat function \"%s\" in file \"%s.gsc\"\n", function.data(), file.data()); + } } else { - console::warn("\tat unknown location %p\n", pos); + console::warn("\tat function \"%s\" in file \"%s.gsc\"\n", function.data(), file.data()); } } } diff --git a/src/client/component/gsc/script_loading.cpp b/src/client/component/gsc/script_loading.cpp index 2037200b..127d9e1c 100644 --- a/src/client/component/gsc/script_loading.cpp +++ b/src/client/component/gsc/script_loading.cpp @@ -27,7 +27,13 @@ namespace gsc std::unordered_map main_handles; std::unordered_map init_handles; - std::unordered_map loaded_scripts; + struct loaded_script_t + { + game::ScriptFile* ptr; + std::map devmap; + }; + + std::unordered_map loaded_scripts; utils::memory::allocator script_allocator; struct @@ -97,11 +103,44 @@ namespace gsc return false; } + std::map parse_devmap(const xsk::gsc::buffer& devmap) + { + auto devmap_ptr = devmap.data; + + const auto read_32 = [&]() + { + const auto val = *reinterpret_cast(devmap_ptr); + devmap_ptr += sizeof(std::uint32_t); + return val; + }; + + const auto read_16 = [&]() + { + const auto val = *reinterpret_cast(devmap_ptr); + devmap_ptr += sizeof(std::uint16_t); + return val; + }; + + std::map pos_map; + + const auto devmap_count = read_32(); + for (auto i = 0u; i < devmap_count; i++) + { + const auto script_pos = read_32() - 1; + const auto line = read_16(); + const auto col = read_16(); + + pos_map[script_pos] = {line, col}; + } + + return 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()) { - return itr->second; + return itr->second.ptr; } try @@ -119,26 +158,29 @@ namespace gsc data.assign(source_buffer.begin(), source_buffer.end()); const auto assembly_ptr = compiler.compile(real_name, data); - const auto output_script = assembler.assemble(*assembly_ptr); + [[maybe_unused]] const auto& [script, stack, devmap] = assembler.assemble(*assembly_ptr); const auto script_file_ptr = static_cast(script_allocator.allocate(sizeof(game::ScriptFile))); script_file_ptr->name = file_name; - script_file_ptr->len = static_cast(output_script.second.size); - script_file_ptr->bytecodeLen = static_cast(output_script.first.size); + script_file_ptr->len = static_cast(stack.size); + script_file_ptr->bytecodeLen = static_cast(script.size); - const auto stack_size = static_cast(output_script.second.size + 1); - const auto byte_code_size = static_cast(output_script.first.size + 1); + const auto stack_size = static_cast(stack.size + 1); + const auto byte_code_size = static_cast(script.size + 1); script_file_ptr->buffer = static_cast(script_allocator.allocate(stack_size)); - std::memcpy(const_cast(script_file_ptr->buffer), output_script.second.data, output_script.second.size); + std::memcpy(const_cast(script_file_ptr->buffer), stack.data, stack.size); script_file_ptr->bytecode = allocate_buffer(byte_code_size); - std::memcpy(script_file_ptr->bytecode, output_script.first.data, output_script.first.size); + std::memcpy(script_file_ptr->bytecode, script.data, script.size); script_file_ptr->compressedLen = 0; - loaded_scripts[file_name] = script_file_ptr; + loaded_script_t loaded_script{}; + loaded_script.ptr = script_file_ptr; + loaded_script.devmap = parse_devmap(devmap); + loaded_scripts.insert(std::make_pair(file_name, loaded_script)); return script_file_ptr; } @@ -153,7 +195,7 @@ namespace gsc std::string get_raw_script_file_name(const std::string& name) { - if (name.ends_with(".gsh")) + if (name.ends_with(".gsh") || name.ends_with(".gsc")) { return name; } @@ -163,10 +205,17 @@ namespace gsc std::string get_script_file_name(const std::string& name) { - const auto id = gsc_ctx->token_id(name); + std::string script_name = name; + if (script_name.ends_with(".gsc")) + { + const auto dot_idx = script_name.find_last_of('.'); + script_name = script_name.substr(0, dot_idx); + } + + const auto id = gsc_ctx->token_id(script_name); if (!id) { - return name; + return script_name; } return std::to_string(id); @@ -319,7 +368,7 @@ namespace gsc ? xsk::gsc::build::dev : xsk::gsc::build::prod; - gsc_ctx->init(comp_mode, [](const std::string& include_name) + gsc_ctx->init(comp_mode, [](const xsk::gsc::context* context, const std::string& include_name) -> std::pair> { const auto real_name = get_raw_script_file_name(include_name); @@ -388,6 +437,17 @@ namespace gsc return game::DB_FindXAssetHeader(type, name, allow_create_default).scriptfile; } + std::optional*> get_script_devmap(const std::string& name) + { + const auto iter = loaded_scripts.find(name); + if (iter == loaded_scripts.end()) + { + return {}; + } + + return {&iter->second.devmap}; + } + class loading final : public component_interface { public: diff --git a/src/client/component/gsc/script_loading.hpp b/src/client/component/gsc/script_loading.hpp index 9bda4f47..1065b639 100644 --- a/src/client/component/gsc/script_loading.hpp +++ b/src/client/component/gsc/script_loading.hpp @@ -5,8 +5,16 @@ namespace gsc { + struct col_line_t + { + std::uint16_t line; + std::uint16_t column; + }; + extern std::unique_ptr gsc_ctx; extern game::dvar_t* developer_script; game::ScriptFile* find_script(game::XAssetType type, const char* name, int allow_create_default); + + std::optional*> get_script_devmap(const std::string& name); }