diff --git a/deps/extra/gsc-tool/interface.cpp b/deps/extra/gsc-tool/interface.cpp index 209f2c99..28b7ad73 100644 --- a/deps/extra/gsc-tool/interface.cpp +++ b/deps/extra/gsc-tool/interface.cpp @@ -13,8 +13,18 @@ namespace gsc return compiler; } + std::unique_ptr decompiler() + { + return std::make_unique(); + } + std::unique_ptr assembler() { return std::make_unique(); } + + std::unique_ptr disassembler() + { + return std::make_unique(); + } } diff --git a/deps/extra/gsc-tool/interface.hpp b/deps/extra/gsc-tool/interface.hpp index 4d98ad35..133e6ae2 100644 --- a/deps/extra/gsc-tool/interface.hpp +++ b/deps/extra/gsc-tool/interface.hpp @@ -3,5 +3,7 @@ namespace gsc { std::unique_ptr compiler(); + std::unique_ptr decompiler(); std::unique_ptr assembler(); + std::unique_ptr disassembler(); } diff --git a/src/client/component/gsc.cpp b/src/client/component/gsc.cpp index b9123766..c8a45f66 100644 --- a/src/client/component/gsc.cpp +++ b/src/client/component/gsc.cpp @@ -13,7 +13,10 @@ #include #include +#include #include +#include +#include #include #include @@ -31,7 +34,9 @@ namespace gsc game::dvar_t* developer_script = nullptr; auto compiler = ::gsc::compiler(); + auto decompiler = ::gsc::decompiler(); auto assembler = ::gsc::assembler(); + auto disassembler = ::gsc::disassembler(); std::unordered_map main_handles; std::unordered_map init_handles; @@ -157,6 +162,40 @@ namespace gsc return script_file_ptr; } + std::string get_script_file_name(const std::string& name) + { + const auto id = xsk::gsc::h1::resolver::token_id(name); + if (!id) + { + return name; + } + + return std::to_string(id); + } + + std::vector decompile_script_file(const std::string& name, const std::string& real_name) + { + const auto* script_file = game::DB_FindXAssetHeader(game::ASSET_TYPE_SCRIPTFILE, name.data(), false).scriptfile; + if (!script_file) + { + throw std::runtime_error(std::format("Could not load scriptfile '{}'", real_name)); + } + + console::info("Decompiling scriptfile '%s'\n", real_name.data()); + + std::vector stack{script_file->buffer, script_file->buffer + script_file->len}; + std::vector bytecode{script_file->bytecode, script_file->bytecode + script_file->bytecodeLen}; + + auto decompressed_stack = xsk::utils::zlib::decompress(stack, static_cast(stack.size())); + + disassembler->disassemble(name, bytecode, decompressed_stack); + auto output = disassembler->output(); + + decompiler->decompile(name, output); + + return decompiler->output(); + } + void load_script(const std::string& name) { if (!game::Scr_LoadScript(name.data())) @@ -531,7 +570,7 @@ namespace gsc real_name = xsk::gsc::h1::resolver::token_name(id); } - const auto script = load_custom_script(name, real_name); + auto* script = load_custom_script(name, real_name); if (script) { return script; @@ -619,7 +658,15 @@ namespace gsc std::string file_buffer; if (!read_scriptfile(real_name, &file_buffer) || file_buffer.empty()) { - throw std::runtime_error(std::format("could not load gsc file '{}'", real_name)); + const auto name = get_script_file_name(include_name); + if (game::DB_XAssetExists(game::ASSET_TYPE_SCRIPTFILE, name.data())) + { + return decompile_script_file(name, real_name); + } + else + { + throw std::runtime_error(std::format("Could not load gsc file '{}'", real_name)); + } } std::vector result; @@ -744,6 +791,7 @@ namespace gsc { if (free_scripts) { + xsk::gsc::h1::resolver::cleanup(); clear(); } });